21

I am looking for a way to create deferred object which will be resolved outside the current scope. I like deferred objects and as I see Promise.defer() in Chrome 38 returns the deferred object.

But in latest Firefox 34 Promise.defer is undefined as well in Safari 8.0.

So I can't use Promise.defer everywhere now. What about future status? Will it be implemented in other browsers or will be removed as deprecated?

just-boris
  • 9,468
  • 5
  • 48
  • 84

2 Answers2

36

Although I doubt this is a good idea, but technically you can implement custom deferred object based on Promises. For example:

function defer() {
    var deferred = {};
    var promise = new Promise(function(resolve, reject) {
        deferred.resolve = resolve;
        deferred.reject  = reject;
    });
    deferred.promise = promise;
    return deferred;
}

var deferred = defer();

deferred.promise.then(function(data) {
    document.body.innerHTML += '<p>Resolved: ' + data + '</p>';
});

document.body.innerHTML = '<p>Deferred created.</p>';

setTimeout(function() {
    deferred.resolve(123);
}, 2000);
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • 1
    this is considered a Promise anti-pattern because it doesn't allow for correct propagation of errors – Alnitak Jan 11 '15 at 17:39
  • 4
    @Alnitak I know it, that's why I said it's not a good idea. I just demonstrated that it's technically possible to implement deferred object. – dfsq Jan 11 '15 at 17:52
  • @Alnitak it has nothing to do with propagation of errors - you're mistaking the deferred anti pattern with constructing a promise with a deferred. – Benjamin Gruenbaum Jan 11 '15 at 18:42
  • @BenjaminGruenbaum And since you are here, it would be really interesting, how you would answer OP question? Because my solution feels awkward. – dfsq Jan 11 '15 at 18:45
  • @BenjaminGruenbaum oops, misremembered what the problem was – Alnitak Jan 11 '15 at 18:46
  • 1
    @dfsq I upvoted your solution for being pragmatic and the solution provided by Alexander O'Mara is correct. I'm not sure what I can further add here. – Benjamin Gruenbaum Jan 11 '15 at 19:18
  • Thanks for answers! But when I know the risk about uncaught exceptions, can I extract deferred like shown there? – just-boris Jan 11 '15 at 19:21
  • @just-boris yes - the promise constructor is _guaranteed_ to run the code passed to it synchronously so the above approach is guaranteed to work successfully. Extracting a deferred was one of the main considerations when the constraint of running it synchronously was placed. This is guaranteed to work in every browser and in every library implementing the promise constructor specification. – Benjamin Gruenbaum Jan 11 '15 at 19:28
  • For future readers pondering on this issue. The mentioned Promise anti-pattern related to uncaught exceptions. It happens when the initiating callback function throws an exception and it should go to the reject handler but it doesn't. More info at http://bytearcher.com/articles/pitfalls-of-promisifying-by-band/ – pspi Nov 05 '15 at 09:06
  • This helps me.I'm asked to use a sharepoint package,encounter a case where the call function is executeQueryAsync,succeeded and fail response are in two separated function.I got to use deferred to set succeeded and fail response one by one. – sbk201 Jul 31 '18 at 09:14
  • This is great for unit testing state when promises are in flight. Like checking if that button is disabled while waiting for the promise to return. – CaptRespect Jul 30 '21 at 15:05
  • http://bluebirdjs.com/docs/api/deferred-migration.html – danday74 Aug 23 '23 at 23:44
28

According to the MDN article on Deferred, the .defer method is obsolete. If you look at this bug issue, it says that Promise.defer is non-standard, so it's not likely to return.

Starting from Gecko 30, this object is obsolete and should not be used anymore. Use the new Promise() constructor instead.

They offer an example of how to rewrite Promise.defer code, to instead use new Promise.

Promise.defer

var deferred = Promise.defer();
doSomething(function cb(good) {
    if (good)
        deferred.resolve();
    else
        deferred.reject();
});
return deferred.promise;

new Promise

return new Promise(function(resolve, reject) {
    doSomething(function cb(good) {
        if (good)
            resolve();
        else
            reject();
    });
});

There are several advantages to the new format, including cleaner code, and improved throw safety (if the code in the promise init function throws synchronously the promise will reject).

Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
  • 16
    It's worth mentioning _why_ it was removed - the promise constructor is throw safe unlike the deferred. If you throw in the scope of the promise constructor it will convert it to a rejection for you. – Benjamin Gruenbaum Jan 11 '15 at 18:41
  • @BenjaminGruenbaum, thanks, that's really what I mean. – just-boris Jan 11 '15 at 19:22
  • 2
    more complete why: [Difference between defer().promise and Promise](http://stackoverflow.com/q/28687566/1048572) – Bergi Feb 24 '15 at 13:10
  • 20
    However the ability to resolve or reject a promise (deferred) at a distance, óutside the constructor, is lost. – Michahell Mar 22 '18 at 14:22
  • 2
    So when promise needs to be resolved/rejected outside of the constructor, what do we do now? dfsq's answer? – Greendrake Jul 19 '20 at 08:51
  • http://bluebirdjs.com/docs/api/deferred-migration.html – danday74 Aug 23 '23 at 23:46