6

It seems generally that creating deferred objects is now commonly discouraged in favor of using the ES6-style Promise constructor. Does there exist a situation where it would be necessary (or just better somehow) to use a deferred?

For example, on this page, the following example is given as justification for using a deferred:

function delay(ms) {
    var deferred = Promise.pending();
    setTimeout(function(){
        deferred.resolve();
    }, ms);
    return deferred.promise;
}

However, this could be done just as well with the Promise constructor:

function delay(ms) {
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve();
        }, ms);
    });
}
xyz
  • 1,513
  • 2
  • 13
  • 17
  • The cases where you'd use the promise constructor are pretty rare too. For what it's worth. – Benjamin Gruenbaum Sep 29 '15 at 21:02
  • As Benjamin Gruenbaum said, you would seldom need to even use `new Promise`. Your example above is a case in point. A delay promise that resolves to `undefined` is not very useful. You would typically want to delay an existing promise e.g. `somPromise.delay(t).then(...)`, in which case your delay would not create a new promise. If you need to start a promise chain where you don't have a promise, you can simply use `Promise.resolve` or `Promise.reject` – caasjj Sep 29 '15 at 21:26
  • `function() { resolve(); }` is equivalent to `resolve` in this case. –  Sep 30 '15 at 03:44
  • Possible duplicate of http://stackoverflow.com/questions/31069453/creating-a-es6-promise-without-starting-to-resolve-it – Roamer-1888 Sep 30 '15 at 13:39
  • 1
    @caasjj That's true for some promise implementations, but not all of them provide a `.delay()`-type method. ES6 native promises surely don't. – JLRishe Jul 17 '16 at 06:16

2 Answers2

7

Does there exist a situation where it would be necessary (or just better somehow) to use a deferred?

There is no such situation where a deferred is necessary. "Better" is a matter of opinion so I won't address that here.

There's a reason that the ES6 promise specification does not have a deferred object. You simply don't need one. Anything that people used to use a deferred object for can always be done another way that doesn't use a deferred object.

First off, the majority of uses fit very nicely into the promise constructor model. Secondly, any other cases that didn't fit quite so cleanly into that model can still be accomplished with the promise constructor or by starting with a resolved promise and chaining to it.

The main use case I've seen for a deferred is when you want to pass the ability to resolve() or reject() off to some other piece of code other than the code that created the promise. A Deferred made that very easy since you could just pass the deferred object and it had public methods for resolving or rejecting it. But, with a promise, you can also just pass the resolve and/or reject methods. Since they are bound to the specific object automatically, you can just pass the function references. And, in other cases, you can just let the other code create their own promise and resolve/reject it themselves and have that operation linked to yours rather than letting them resolve/reject your promise. Is all that quite as clean as passing a deferred object? Mostly a matter of opinion, but neither are very common use cases and all are something that can be accomplished without a separate deferred object.

And, as torazaburo points out, letting some external code resolve or reject your promise is a bit of an anti-pattern in its own right. You created the promise - you resolve/reject it. If you want to use external events to decide when to resolve/reject it, then have them notify you (via their own promise or callback) and you can resolve/reject your own promise. Or, have them create their own promise that you can use. That's really the desired model. Or, let them chain onto your promise so that the end result is gated by when their operation is done.

If one was used to coding with the deferred object (say with a jQuery deferred), it might take a little getting used to coding without it, but after a little while you just start to think differently and it starts to come natural to just use the promise constructor.

Once you promisify the sphere of async operations that you use in any given application, it's pretty rare that you ever even need to create your own promises any more because you are mostly just building off promises that the async functions you call are already creating or using flow control operations like Promise.all() which create uber promises for you.

This is the main point of anti-patterns. Use the promises that are already created for you rather than manually create more. Chain them. Return promises from .then() handlers to link async operations under logic control.

Of course, there are existing async operations that don't return promises for which somebody needs to create a promise, but that should be done in a promisify layer somewhere outside the purvey of your main coding logic and done only once for that operation and then code that calls the async operation should be able to just use the returned promises.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Yes, great explanation. But with regard to *pass the ability to resolve() or reject() off to some other piece of code other than the code that created the promise*, it's worth pointing out that this itself is something of an anti-pattern. The design of the promise constructor is intended to promote encapsulating all the promise logic within the callback, at least to the extent that is possible, which is almost all the time. –  Sep 30 '15 at 03:48
  • 1
    @torazaburo - I agree. Since I weaned myself off the jQuery Deferred object and just got used to a different way of coding things, I've never found a reason to use or need that pattern of coding. If you are using external things to trigger resolving the promise you created, you can always have them notify you some other way (via their own promise or via a callback) and you can then resolve your own promise. But, it seems we both hesitate to say unequivocally that there is never such a valid case (we really can't ever prove the lack of such a case), so thus I offered a work-around just in case. – jfriend00 Sep 30 '15 at 04:13
  • @jfriend00 how about promises for promises that are yet to be created? E.g., "canned" network requests that should be fired at some point in the future: `/* in a function that prepares the request */ var deferred = Promise.defer(); function makeRequest() { doMakeRequest().then(function(data) {deferred.resolve(data)}) .catch(function(e) {deferred.reject(e)}); } queue.push(request); return deferred.promise; ..... /* at some point later in another galaxy */ makeRequest = queue.shift(); makeRequest(); ` How it is possible with a synchronous Promise constructor? – mderk May 24 '16 at 15:27
  • @mderk - If you describe a real world problem, then I could take a shot at how to solve it without using a deferred. I've personally never encountered a need for a promise of a promise (where chaining couldn't be used) so I have no idea what the real world situation would be and therefore can't really offer you an appropriate alternative. I could come up with a theoretical solution to your theoretical problem, but that doesn't seem useful because your description of the code so far assumes a lot of the implementation which likely does not have to be done that way if the whole problem is known. – jfriend00 May 24 '16 at 22:14
  • @mderk - Also, if you want to discuss a specific problem and how to solve it without a deferred, then I'd suggest you post a new question with your problem described and then leave me a comment that notifies me of the new question because I don't think you can fully describe what you're trying to do in just a comment here. – jfriend00 May 24 '16 at 22:18
  • @jfriend00 here it is: http://stackoverflow.com/questions/37426037/promises-for-promises-that-are-yet-to-be-created-without-using-the-deferred-ant – mderk May 25 '16 at 00:34
  • @jfriend00 You say *exposing* your promise controls to an outsider, so they can resolve your promise, is bad practice . And that *inyecting* an outside promise to your controls so they are triggered when it resolves, is good practice. But I think they are exactly the same thing. Ultimately, your promise depends on an outsider. Exposing your controls outside is in no way worse than injecting outside into your controls. – Juan Perez Jan 23 '23 at 17:14
4

Native Promises don't have all the built-in methods that deferred has out of the box, for example the method to resolve the promise outside the scope of its constructor (very easy to implement with native Promise, though), and the ability to look at the status of a promise (which is hidden from regular JavaScript on native Promises, though it can be inspected in the dev tools).

The main reason to use deferred today would be backwards compatibility with code that depends on those extra methods. Hard to give a definitive answer to your question without stepping onto opinion territory.

Touffy
  • 6,309
  • 22
  • 28