简体   繁体   中英

Will Javascript ES6 promise support 'done' api?

For example

p = new Promise(function (resolve, reject) {
    throw 'err';
});

p.done();

In most promise polyfill libs, the done will throw an error, and the current execution will exit.

But if we use p.then() , nothing will happen. The error is swallowed by the promise. If we use p.catch , we have no way to exit current execution. I want to achieve something like:

try {
    // something
} catch (err) {
    if (check(err)) {
        throw err;
    }
}

No.

Not only will .done likely not be supported in the future versions of the spec - it is unneeded. Quoting from the threads Mariusz linked to:

Domenic:

it's still error-prone: if you slip up and don't follow the rule even once, you might silence an error forever.

Mark Miller (who pioneered the concept of promises):

Note that weak-refs, hopefully in ES7, will provide us one of the diagnostic tools we need to bridge this gap. Using weak-refs, if a rejected promise gets collected without having notified any handlers, we can arrange that this generates a diagnostic. The promise implementation would have to keep the reason in the promise's executor (post-mortem gc handler), so that it has the diagnostic to report after discovery that the promise has been rejected.

Yehuda Kats on RSVP's error handler:

The approach we're taking in RSVP is to install an unhandled promise monitor that throws by default.

You can opt a particular promise out of this behavior by attaching a noop failure handler, if you know that you will be attaching asynchronous error handlers. We will probably have sugar for this (.undone :p)

In our experience, moving the burden from literally everyone to people who may want to attach async error handlers is appropriate.

And, from the actual repo that preceded the spec, Domenic said:

done's job will be done by integrating unhandled rejection tracking functionality into dev tools. Most TC39ers, from what I understand, as well as myself, perceive that as enough for the spec to be complete.


The spec committee did not just ignore .done , they deemed it was unnecessary and error prone. New modern promise libraries automatically detect unhandled rejections - two examples of this are When promises and Bluebird promises that pioneered the idea.

.done is an artifact - originating from the fact the browser could not detect unhandled rejections. Truth is - detecting them deterministically is impossible but for the vast majority of cases it is completely possible.

Don't believe me? Open Firefox and play with its native promises:

p = new Promise(function (resolve, reject) {
    throw 'err';
});
// Logs as error: Unhandled error: `err`

Simply put - firefox uses garbage collection hooks in order to determine promises were disposed in an unhandled state and fires a global error handler which defaults to writing on the screen.

Now, the problem is native promises are not very usable yet - since in IE they don't exist and in Chrome unhandled rejection detection was not yet implemented - but it's coming and it'll be there. Meanwhile you can use an ES6 compatible library like Bluebird which will do this rejection tracking for you.

If you want to polyfill done (which I strongly recommend against) - the polyfill by torazaburo has a few shortcomings. It declares an enumerable property on the promise prototype and generally this is not how the spec was designed - you are expected to subclass promises in order to extend them rather than monkey patch them - sadly no implementations currently support this.

So in short:

  • Wait for native promises to stabilize before you use them - in the meanwhile you can use libraries that implement the spec like Bluebird. When it stabilizes not having .done will not be an issue at all.
  • Utilize patterns for detecting errors - for example check out the disposer pattern here.
  • Use the developer tools when available, long stack traces and and async debugging are big plusses. Also note you should not throw strings if you want meaningful stack traces.

Good luck and happy coding.

No, AFAIK done is not part of the spec. To mimic its behavior, you should throw the exception on the next tick, outside the purview of the promises chain:

p.catch(function(e) { 
    setTimeout(function() { throw e; });
});

This is essentially how libraries implement done . See excerpt from Q documents:

Much like then , but ... the resulting rejection reason is thrown as an exception in a future turn of the event loop .

Implementing done yourself

If you want to implement the approximate semantics of done as typically understood, then something like:

Promise.prototype.done = function(onFulfilled, onRejected) {
    this
        .then(onFulfilled, onRejected)
        .catch(function(e) {
            setTimeout(function() { throw e; });
        })
    ;
};

Setting up an error handler

If you want a chance to handle these errors yourself, you could set up an error handler:

Promise.onError = function(e) {
    console.log("The sky is falling", e);
    throw e;
};

Then invoke the handler on the next tick:

Promise.prototype.done = function(onFulfilled, onRejected) {
    this
        .then(onFulfilled, onRejected)
        .catch(function(e) {
            setTimeout(Promise.onError || function() { throw e; }, 1, e);
        })
    ;
};

Current statement of TC39 is that this issue can be and should be solved natively in browser engines with developer tools. That's why they're opposite providing done within native API.

It's indeed controversial decision, see following links for discussions on that matter:

https://github.com/domenic/promises-unwrapping/issues/19

http://mozilla.6506.n7.nabble.com/Where-d-Promise-done-go-td281461.html

https://github.com/promises-aplus/promises-spec/issues/43

https://github.com/slightlyoff/Promises/issues/33

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM