Skip to content

Promise.deferred()? #80

@juandopazo

Description

@juandopazo

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?

Activity

domenic

domenic commented on Dec 2, 2013

@domenic
Owner

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.

petkaantonov

petkaantonov commented on Dec 2, 2013

@petkaantonov

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

juandopazo

juandopazo commented on Dec 2, 2013

@juandopazo
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

stefanpenner commented on Dec 2, 2013

@stefanpenner

@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

domenic commented on Dec 2, 2013

@domenic
Owner

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

juandopazo commented on Dec 2, 2013

@juandopazo
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

stefanpenner commented on Dec 2, 2013

@stefanpenner

@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

juandopazo commented on Dec 2, 2013

@juandopazo
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

erights commented on Dec 2, 2013

@erights
Collaborator

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

juandopazo commented on Dec 2, 2013

@juandopazo
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

vvo commented on Nov 11, 2014

@vvo

@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

benjamingr commented on Nov 11, 2014

@benjamingr

@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

vvo commented on Nov 11, 2014

@vvo

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

benjamingr commented on Nov 11, 2014

@benjamingr

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

vvo commented on Nov 11, 2014

@vvo

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

benjamingr

benjamingr commented on Nov 11, 2014

@benjamingr

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

vvo commented on Nov 11, 2014

@vvo

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

benjamingr commented on Nov 11, 2014

@benjamingr

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

vvo commented on Nov 11, 2014

@vvo

While I don't think your use is particularly good

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

benjamingr

benjamingr commented on Dec 29, 2020

@benjamingr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @stefanpenner@vvo@erights@juandopazo@domenic

        Issue actions

          Promise.deferred()? · Issue #80 · domenic/promises-unwrapping