Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.deferred()? #80

Closed
juandopazo opened this issue Dec 2, 2013 · 20 comments
Closed

Promise.deferred()? #80

juandopazo opened this issue Dec 2, 2013 · 20 comments
Labels

Comments

@juandopazo
Copy link

The only use case I can think of the current API doesn't address is handing out the capability to resolve/reject a promise. We can of course do:

var resolve, reject, promise;

promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

...but it's a bit awkward. Should we add Promise.deferred exposing GetDeferred?

@domenic
Copy link
Owner

domenic commented Dec 2, 2013

The reasoning behind not adding deferreds is that they are usually are an anti-pattern and, even worse, an extra concept to learn. They’ve proven troublesome in user-space, and the promise constructor has proven to be a great fix. The only reason the spec uses it so much is because creating closures in ECMASpeak is a pain, so we encapsulate that in a single operation. This in turn causes us to have to manually re-capture exceptions and forward them to deferred.reject (see e.g. all the RejectIfAbrupts in Promise.all and Promise.race). Bad news bears.

@domenic domenic closed this as completed Dec 2, 2013
@petkaantonov
Copy link

I should really change the rantish and hostile tone in that article :D

@juandopazo
Copy link
Author

I totally share the sentiment of that article, but the bad practice still happens with the promise constructor. It just looks like this:

function someFunction() {
  return new Promise(function (resolve, reject) {
    otherPromise.then(function (value) {
      resolve(doSomethingWithTheValue(value));
    ], reject);
  });
}

@stefanpenner
Copy link

@juandopazo or

function someFunction() {
  return new Promise(function (resolve) {
    resolve(otherPromise.then(SometingWithTheVlaue));
  });
}

or with something like Promise.method

foo: Promise.method(function(){
  return otherPromise.then(somethingWithTheValue);
});

Additionally, the Promise constructor approach catches exceptions and propagates them as one would expect.

@domenic
Copy link
Owner

domenic commented Dec 2, 2013

Additionally, the Promise constructor approach catches exceptions and propagates them as one would expect.

This is one of the stronger arguments against deferreds, in practice.

@juandopazo
Copy link
Author

the Promise constructor approach catches exceptions and propagates them as one would expect

That´s a good point.

I guess sharing resolve is an obscure enough practice so adding a shorthand could do more harm than good.

@stefanpenner
Copy link

@domenic ya.

Additionally, it appears that modeling the promise as the thing that is the potential future value results in much more understandable and maintainable code. Using a deferred seems to almost always spiral away from this. Nearly each time I see a deferred used, it is being used as a lock/semaphore and in practice this seems to result in very hard to digest patterns.

@juandopazo
Copy link
Author

Nearly each time I see a deferred used, it is being used as a lock/semaphore

Same here. I haven't found it hard to digest in the cases I ran into, but it's definitely not a good idea.

@erights
Copy link
Collaborator

erights commented Dec 2, 2013

Hi Stefan, could you give an example of using a deferred as a
lock/semaphore? Thanks.

On Mon, Dec 2, 2013 at 7:57 AM, Stefan Penner notifications@github.comwrote:

@domenic https://github.com/domenic ya.

Additionally, it appears that modeling the promise as the thing that is
the potential future value results in much more understandable and
maintainable code. Using a deferred seems to almost always spiral away from
this. Nearly each time I see a deferred used, it is being used as a
lock/semaphore and in practice this seems to result in very hard to digest
patterns.


Reply to this email directly or view it on GitHubhttps://github.com//issues/80#issuecomment-29629228
.

Text by me above is hereby placed in the public domain

Cheers,
--MarkM

@juandopazo
Copy link
Author

@erights I recently saw something like this:

function SomeConstruct() {
  var deferred = Promise.deferred();
  this._deferred = deferred;
  this.ready = deferred.promise;
}

SomeConstruct.prototype.doSomething = function () {
  var self = this;
  return this.someThingElse().then(function () {
    self._deferred.resolve();
    //...
  });
};

var foo = new SomeConstruct();
foo.ready.then(...);

@vvo
Copy link

vvo commented Nov 11, 2014

@petkaantonov Hi, is this still considered as an anti-pattern to use deferreds as observers?

var pending = {};
pending._promise = new Promise(function(resolve, reject){
  pending._resolve = resolve;
});
pending.ready = function() {
  pending._resolve(true);
}
pending.onReady = function(fn) {
  pending._promise.then(fn);
}

pending.onReady(function() {
  console.log('YOLO PATTERN!')
});

setTimeout(pending.ready, 200);

Or is this only taste?

How would you implement this otherwise in a nice manner?

Thanks.

@benjamingr
Copy link

@vvo why the extra layer for onReady?

var pending = {}; // not sure what pending is. 
pending.ready = new Promise(function(resolve){ setTimeout(resolve, 200); });
pending.ready.then(fn); // instead of `.onReady(fn)

@vvo
Copy link

vvo commented Nov 11, 2014

Was for the example, did not wanted to expose the underlying implementation (promise).

So that, you want to change the implementation with say, array of callbacks, you can.

Also I do not want the API consumer to use the _promise.

@benjamingr
Copy link

Why would you not want to expose the fact it's a promise? A promise is generic.

You can always do: pending.ready = pending.ready.then.bind(pending); but I really don't see the point.

@vvo
Copy link

vvo commented Nov 11, 2014

@benjamingr it's only encapsulation, we are missing the point of the deferred anti-pattern here :) was just an example.

@benjamingr
Copy link

I'm trying to understand your use case here - why do you need a deferred in the first place?

The deferred anti pattern is about creating an excess deferred for wrapping an existing promise in another promise where .then can be used instead. If you're creating a promise based on a callback API like setTimeout and automatic promisification facilities are unavailable - you have to use a deferred or the promise constructor.

@vvo
Copy link

vvo commented Nov 11, 2014

I am using Promise and deferred pattern only to not have to implement myself an array of callbacks + ready flag condition + foreach callback. I do not wrap an existing promise.

The way the example is written is only to be able to hide the implementation of my observer object. Like a lot of objects say in Node.js core are "hiding" (_prefixing) some properties. This is a completely different subject.

I only wanted to show how deferred can be used to solve some problems and that it's not always an antipattern.

@benjamingr
Copy link

If you're not wrapping another promise than you are not doing the deferred anti-pattern, deferred is not an anti-pattern itself..

While I don't think your use is particularly good - it׳s not related to what Petka's article discusses.

@vvo
Copy link

vvo commented Nov 11, 2014

While I don't think your use is particularly good

So you would implement this using an array of callbacks right?

@benjamingr
Copy link

So, not to stir up an old discussion but deno is using Deferreds and when I raised the issue of throw safety it looks like they are using them like locks.

@stefanpenner asked about using promises as semaphores and @erights asked for an example but I don't actually see a conclusion of what's the "better" way to do it.

Would appreciate some guidance/ideas/thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants