We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

timmywil • 10 years ago

*[Drumroll stops]* Okay, I feel better now.

Furkan Tunalı • 8 years ago

drumroll.then((feel) => { return feel === 'better'});

Erin Swenson-Healey • 10 years ago

Hi Jake,

Have promises made it into the ES6 Draft Specification? Or is this something that browser vendors have decided to implement independently?

I fear that without pressure to adhere to the ECMA spec that browser implementations could become divergent. Perhaps with the Promises/A+ spec this is less of an issue. What are your thoughts? I'd hate to consume these native APIs and then have them change out from underneath me.

Erin

Domenic Denicola • 10 years ago

Hi Erin, ES6 Promises spec editor here. The spec is developed on GitHub at domenic/promises-unwrapping. Allen is working on translating it from Markdown into Word, and doing other integration work, hopefully in time for the next draft snapshot.

Erin Swenson-Healey • 10 years ago

Hi Domenic,

Thanks for the link. I wasn't seeing anything in the most recent ES6 working draft.

Erin

Guest • 10 years ago

Here's the spec https://github.com/domenic/... - as far as I can tell all browsers are happy with it, as much as the rest of es6 anyway.

Chris Perkins • 10 years ago

Hmmm. I work with two projects where we use promises a lot. One uses Q and one jQuery Deferreds. I use them extensively, and I far prefer them to callbacks, or, worse, synchronous blocking code. But overall, I'm not enthusiastic about promises.

A couple observations:

1 - The automatic error capture of the Q promises seems like a nice feature, but it requires discipline to use it correctly. If a .done() call is overlooked at the end of the promise chain then any error that was caught by Q in that chain will be eaten by it and go unreported. The jQuery deferreds don't do this. This is supposed to be a big advantage of Q, but I've found it to be the reverse. The Q error harness causes more trouble than it solves.

2 - In your example, the synchronous code is pretty easy to read. But by the time we are done with adding all the promises, it's not. It's not your fault, and I'm not saying callbacks would be better. But promises leave a lot to be desired. If JS was truly parallel (instead of single threaded) we could have Futures, like they do in Clojure (and other languages). A future blocks only when/if it is (de)referenced. You get the same functionality, but with a much less intrusive syntax. But JS is single threaded, so blocking is a no-no.

3 - In my experience, once you introduce a promise into a call chain, it infects it up and down. This wreaks havoc. "D***it, this latest feature can be done with a one-line change to load from a file. But now we've got to touch everything to handle the promise."

So, the end result is that now APIs that could be synchronous and simple are instead specified as returning promises...just in case. Better to over-specify than have to change the API later, right?

4 - Instead of promises, for a certain class of operations we can use CPS transforms. ( I hacked one awhile ago https://github.com/cperkins... ) . The readability is better. However, there is a considerable downside: these require a pre-processing step. Nor are they generally as useful (promises can be passed as variables, etc). BUT, let me state that instead of the browsers supporting promises, something for which we already have good libraries, I would rather they introduced something like built-in CPS transforms, for which there is no elegant solution. For a large set of use cases, this would have given us what we want with much better readability, and for everything else we could use a promise library.

Esailija • 10 years ago

If you don't like Q's approach to errors, have a look at bluebird. (Disclaimer: I am the author).

TL;DR

- Possibility to choose any kind of error reporting for uncaught errors:
- Eager reporting (the default)
- Late reporting when (actually, that's an IF) GCd (requires module).
- Live array (like in Q, requires module as well)
- Whatever you want since the API is exposed :)
- The promise "catch clauses" can take predicates unlike the try- catch statement in javascript.
Combine with the above and you are no longer swallowing errors
that are bugs vs errors that are normal and expected
- Has a .done() method if you like that (I don't)
- Long stack traces go all the way

Also promise code using arrow functions in practice (read: when there is realistic error handling) is even cleaner than generator code I personally find.

jaffathecake • 10 years ago

1 - I believe the plan is to expose uncaught errors when the promises are garbage collected. This won't suffer from the Q problem, there isn't a done() method at all.

2 - Check out the generators example!

3 - Doesn't the generators thing solve this too?

Chris Perkins • 10 years ago

2 - You are correct. The generators definitely help clear that up. The code reacquires the readability of the original.

3 - And you are correct again. The synchronous routine could wrap its innards in a generator that yields the value, and then spawn and call that generator returning its newly-AJAXED result. So, remaining a synchronous routine.

I hadn't an appreciation for generators, and I wasn't thinking them through fully when I first read your article but after your reply here I've spent a bit of time working with them and they rock. So, thanks for that, taking the time to reply.

medikoo • 10 years ago

I'm a big fan of promises, I use them heavily for over two years, but unfortunately native spec at current state doesn't look as one I would like to use. I see two stopper issues:

1. No synchronous access to resolved value (when it's available). There are use cases when you need it, and having that, you can treat promise objects as reusable value wrappers long after they're resolved (forced asynchronicity on plain value access is never convenient).

2. No means to expose unhandled errors. This was one of big the drawbacks of first JS implementations and one of the reasons why most of the node'rs were saying "no" to the promises.
In your examples you propose to use `then` and `catch` for that, but all they do is *mapping* to another promise, any `throw` in final `then` or `catch` will not be exposed. You really don't want to use promise implementation which mutes such errors.
Today most of JS implementations solve that with `done`, which aside of being dedicated error handler serves as much better base than `then` (`done` and `then` share same analogy as `forEach` and `map` in Array's API). Still `done` (or equivalent) is not in a spec.

jaffathecake • 10 years ago

1. This is part of Promises/A+ and is for consistency. If you want the resolved value to be available sync:


var val;
promise.then(function(v) { val = v; });
// ...
if (val !== undefined) {
// you have sync access
}

2. This will be handled by browser devtools without the need for done(). Eg, when promise objects are garbage collected, uncaught errors will be logged in dev tools. See https://github.com/domenic/... for the discussion.

Mariano Ruiz • 7 years ago

1. Doesn't work, and I agree with medlkoo that in some cases is necessary to get the value in a sync way. Here is an example that you can execute in latest versions of Node.js, Chrome Tool o FF Dev Tools, all of them get the same result:


function giveMeFive(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("5");
}, ms);
});
}
var ms = 3000;
console.log("Give me five in " + ms + " ms ...");
var value;
giveMeFive(ms).then(function(v) {
value = v;
});
if (value !== undefined) {
console.log("The value is " + value);
}
console.log("End of the program :(");

You will get this result in the console (the `console.log("The value is " + value);` instruction is never called):


Give me five in 3000 ms ...
End of the program :(
medikoo • 10 years ago

1. There's no way to access resolved value sync way as you suggest. I'm not sure what you want to show with that example.
2. Firstly, it's not specified so whether there will be some aid in browser devtools, remains just question of good will of browser vendor. Additionally as it's not specified it may appear to be solved in few incompatible ways among browsers. Promise implementation without that cannot be perceived as complete, and same way specification should not.
Second issue that comes with that, is that `then` is promoted as first choice value access function, while it's actually mapping function that has side effects. We should be provided with better utility that doesn't force the baggage of `then`. We must be able to process resolved values in normal circumstances where `throw error` as in any other API we know actually throws error, and does not intercept it leaving its exposure to eventual good will browser dev tools.

jaffathecake • 10 years ago

1. In the example above val will !== undefined when the promise is resolved, and you can access val synchronously. You can use this pattern when you need sync access, works the same way promise.resolvedValue would. If it doesn't work, show me a case where it doesn't.

2. No, it's not specified, but that's also true of XHR, but we do have tools. JavaScript step debugging isn't in the spec either.

medikoo • 10 years ago

1. That's not true. By spec `then` always call its callback arguments earliest in next tick, even if promise is resolved. In your example for promise at any state `val === undefined` will be true.
2. Not true either, event emitter API on which XHR stands, freely throws unhandled errors introduced in programmers code, promise API doesn't, that really makes big difference.

jaffathecake • 10 years ago

Ohh, I see what you mean with 1 now. If the promise resolved sync, you'd still only get the value on next tick. What's the use-case here?

2. No, devtools will tell you about redirects and detailed CORS errors in ways that aren't in the specification. Also, as I said, step debugging is not specified. Not all debugging that we use daily is part of a spec. If promise debugging can be done without done() it should be. Failing that, bring on done().

medikoo • 10 years ago

2. It's not only about painless debugging (which again without being specified is not guaranteed), but about having fine utility methods for custom programming needs. `then`, `catch` aside of intercepting errors, produce another promise which is not needed in many cases.
We need a way to access resolved value without any side effects. Spec in current shape, forces you to use `then` and promises without any guarantee that all implementers will workaround its side effects, it just doesn't sound right, and it's not how we should build low-level API's

Confused • 10 years ago

Sorry but I'm utterly confused by the "yay promises!" code :
(quote)
var storyPromise;

function getChapter(i) {
storyPromise = storyPromise || getJSON('story.json');

return storyPromise.then(function(story) {
return getJSON(story.chapterUrls[i]);
})
}
(unquote)
How in hell is that easier to read (or write for that matter) than a bunch of callbacks?
"return a promise that, when fulfilled, executes a function that fetches more content from the result and returns that."
I'm no JS guru but am I the only for whom this kind of code doesn't make any sense?

Bob_Kerns • 9 years ago

A fine article! But you editorialize:

Note: I'm unconvinced of Promise.race's usefulness; I'd rather have an opposite of Promise.all that only rejects if all items reject.

Well, we can use Promise.race to construct your opposite. Promise.race is extremely important, because it implements essentially 'OR' to Promise.all's 'AND'.

Let's assume a couple utility functions.


// Only resolves on success, nothing on error
function resolved(p) { return new Promise(function (accept) { p.then(accept); }); }

// Only resolves on rejection, nothing on success
function rejected(p) { return new Promise(function (accept) { p.then(function (){}, accept); }); }

// Resolves on success or failure, never rejects
function completed(p) { return new Promise(function (accept) { p.then(accept, accept); }); }

Now, your opposite becomes:


function opposite(ps) {
function failed() { throw new Error("All rejected"); }
return Promise.race(
Promise.all(ps.map(rejected).then(fail)), // All failed case -- forces reject
Promise.race(ps.map(resolved)) // Something succeeded case
).then(function () {
// Report the result of all of them, unless all failed (rejected above)
return Promise.all(ps.map(completed));
});
}

The most important use case is timeouts. Just define a utility function to return a Promise that rejects after a specified time, then do:


return Promise.race([computation(), timeout(10000)]);

to add a timeout to any promise-returning computation.

You can get a bit more fancy, and write one that you call like this:


return timeout(10000, computation());

This has the advantage that you can use a .then() to cancel the timeout if the computation() succeeds. but is a bit less flexible.

You can also use Promise.race() with a promise that you arrange to reject on some condition. For example, if you get an error, you can reject this promise, and allow dependent promise chains to proceed as rejected without waiting for intermediate computations.

Finally, if you have two servers that might satisfy a request, and you want to try them both in parallel to see which one has the answer:


return Promise.race([tryServer(server1), tryServer(server2), timeout(10000)]);

tryServer() should simply not resolve if the server does not have the answer. Promises that may never resolve are very handy with Promise.race() -- but not very useful without it.

If you have a function tryServers() that returns an object like { successes: [...], failures: [...] }, you can get instant notification of failure as well:


function failed() { throw new Error("All failed"); }

var result = tryServers();
return Promise.race([Promise.race(result.successes),
Promise.all(result.failures.map(completed)).then(failed),
timeout(10000)]);

Note that the lists of successes and failures should be promises, each of which resolves ONLY to a success, or ONLY to a failure. You can't sort out successes and failures until they resolve, but you can report successes and failures on separate channels. Returning objects with multiple Promise channels is a useful pattern.


var init = initialize();
var p = init.then(function (state) { return computation(state); });
return {
init: init,
success: resolved(p),
failure: rejected(p)
};

I'd give more detail, and worked-out examples, but that would amount to an entire blog post itself. I hope this is enough to point people in the right direction about the role that Promise.race() takes in the algebra of Promises.

Hopefully, in the past year since you wrote the article, you've already come to realize the value of Promise.race().

Carlos Reynosa • 10 years ago

Nice! Great example of promises through optimizing and re-factoring.

Samuel Hodge • 10 years ago

Do they have any plans for implementing an "always" callback that gets called on either fulfill or reject?

Domenic Denicola • 10 years ago

I think a finally callback (note: very different from an always callback) is a missing piece of the API, but we had to push through something minimal to get consensus in time for ES6. There's still hope of resurrecting it, but given all the politics surrounding promises, I think it's a pretty small hope. The current consensus is pretty fragile, and if we reopen it now, then people are going to start trying to pile on a lot more of their pet APIs, as you can already see happening with Chrome's non-standard implementation.

In any case, the finally method, with implementation, was proposed and briefly discussed at domenic/promises-unwrapping#18, if you're interested.

JeanHuguesRobert • 10 years ago

try/catch/finally, isn't the similarity with error/failure/finally obvious? "politics" is something that calls for "resistance". Be braver! Cheers.

Guest • 10 years ago

It was discussed https://github.com/domenic/..., but a little too late by the sounds of things. I agree it'd be great. done() too.

chang w. doh • 10 years ago

Promise to get back from callback hells. Thanks for details. :)

Shay Doherty • 10 years ago

Very good article.... ES6 Generators are looking very "promising"...
ASYNC/AWAIT in C# is such a nice model for handling aysnc code flow...

Guest • 10 years ago

Moving callback from one place to another is "pivotal moment in the history of web development" o_O I agree with generators, that can help, for example, on the node servers with sync. But promises. Nothing new :)

You think, that it is a good code, that easy to read (I am talking about your final example)? You better to go to lern more about object-oriented programming. It's the same unreadable code, same callbacks, but in other place, in other order. Yes, it's change everything. Pivotal moment. Hah.

Guys, come on, do you all think this is a good code and promises is the revolution? What are you, then, wrote earlier?

CodingBlocks.NET • 8 years ago

Excellent writeup - easy to follow and the animated Chrome / Debug output helps drive it all home. We'll be discussing Javascript Promises in Episode 31 which we plan to record tomorrow night and this tutorial will definitely be referenced during the show. One of the best written, thorough explanations of promises I've seen. Thank you.

Fred Stluka • 9 years ago

Excellent explanation!

--Fred

James Doyle • 9 years ago

Correct me if I am wrong, but you cannot do the following

```
Promise.resolve(arg1, arg2, arg3)
```

You would have to do

```
Promise.resolve({arg1: arg1, arg2: arg2, arg3: arg3})
// some other code
Promise.then(function(obj){
console.log(obj.arg1) // value of arg1
});
```

As far as I can tell, if you want to resolve multiple values, then you have to use an object?

micahhenning • 8 years ago

Or an array.

derekchen14 • 10 years ago

Amazing article! After reading almost a dozen articles on promises, this is one that really helped me "get it". What's a reasonable estimate for when most people's browsers will be able to support this?

Nicholas Johnson • 8 years ago

IE will probably never support it. Edge has it. You'll need to wait for IE11 to die (currently riding at 7%). 2 years?

Rifat Nabi • 10 years ago

There's no notify :( ?

sebasporto • 10 years ago

Hi, thanks for the post, I read a good part of it but not everything so I might have miss this: in most promise libs at the moment you created a deferred, and then you pass a promise to external consumers. In that way the consumer cannot resolve the promise for you. Is that possible with the new Promise API?

Domenic Denicola • 10 years ago

Yes. You create a new promise with var p = new Promise(function (resolve, reject) { ... }); only the code inside the ... can resolve or reject the promise, whereas people who only have a reference to p cannot.

It's a pretty clever trick, originating with the WinJS library. It avoids the conceptual overhead of a deferred (what does that word even mean anyway?), and provides a nice way to prevent accidental synchronous errors since any errors thrown inside the function are transformed into rejections. More discussion on why we didn't add a way to create deferreds at domenic/promises-unwrapping#80.

JeanHuguesRobert • 10 years ago

Clever but expensive. It involves creating two bound functions... Isn't it beeing sligtly paronoid to assume that one need protection because some promise might be resolved by some code that has no right to do that. Hell, who is it that is supposed to resolve the promise except for the promise provider herself? Who, in her right mind, would decide on her own to resolve a promise someone else did? I just don't understand this (costly) protection shield. Please note that the "deferred" solution has the same issue, it also assumes that someone will resolve/reject a promise it never created in the first first. Weird assumptions, don't you think ? I tend to think a a_promise.resolve() and a a_promise.reject() should be public, called by whoever the promise's protocol allow. If one needs a "protected promise", one should create a subclass and redefine resolve()/reject() to throw some access right violation exception, isn't it that way that subclassing is supposed to work: the super class has super powers?

toth • 10 years ago

Are there no onProgress callbacks, as with $.Deferred()?

Domenic Denicola • 10 years ago

There is no progress in promises; they are meant to represent one-and-done async operations. I and others in the promise implementer community believe progress notifications have been found, through many years of long experience, to be a poor fit for the promise model.

You can see some of the arguments against adding progress features to promises in this www-dom thread from back in the day; see especially this reply.

Guest • 10 years ago

No, that may come in future

Valentine Bondar • 9 years ago

"Don't write sneezy code." That was the CLEAREST analogy of threads I have ever read. You're a great writer sir!

Dan Treasure • 10 years ago

If you're looking for a Promise library that is 100% spec compliant to the Promises/ A+ Compliance Test suite and fast as all hell check out Jive's Promise library https://github.com/jive/Jiv...

It's intriguing to note as well that Domenic wrote the compliance test and yet his own Q library doesn't fully meet the tests.

Guest • 10 years ago

Great, now browsers are implementing things can be done in pure JavaScript anyways. What's the point of this? This uses nothing that requires the browser to step in and do work (like native functions such as networking) and just causes overhead for jumping for the JavaScript VM to native C++ code.

A pure JavaScript implementation of the same thing should have much better performance than this could ever have with 0 customization and the possibility to get fragmented by browser-dependent mistakes that already exist (yes, I know it's not stable yet).

If you want to use Promises use a library that suits your needs perfectly, don't pollute the global namespace with APIs we can't achieve on our own.

The specification for this is even called 'DOM Promises' yet the object has nothing to do with the DOM nor does the word DOM exist in the Promise objects name. If ECMA ever did want to create an Object called Promise now they can't. If this is going to be made stable in browsers at least make the class name DOMPromise or something instead of Promise which will most certainly conflict with countless websites out there.

jaffathecake • 10 years ago

The benefit of a native implementation is other native APIs (including DOM ones) can return them. A DOM API cannot return an object that is library dependant.

The spec for this is not called DOM Promises, here it is: https://github.com/domenic/.... If you read my article you'd see the history of DOM promises vs ES6 ones (http://www.html5rocks.com/e.... They're no longer in the DOM, the DOM just uses the ES6 implementation.

Guest • 10 years ago

According to the current and latest official draft of ES6, there is no such thing as Promises. That was a feature that never made it in to the official draft (yet). Looking at the mailing list also has nothing in particular about adding it and talk about it on the ES6 mailing list has been very quiet lately. If there are still movements to get it in to ES6 before it is finalized, it would be very useful to keep it up to date on the regular channels, not a specialized github.

Also in your own links in this article, you point to the chrome dashboard which still references Promises as DOM Promises (which after researching through the chromium bug tracker I can see that it started off as implementing DOM Promises and then moved to the current github one over time). However to avoid confusion it would be very nice to update all documentation related to things you have marked as 'shipped' (which this feature is).

I agree that returning a unified object from all DOM objects makes sense and should be done. However since Promises are not an official feature of ES6 as of yet this can basically be called 'DOM' Promises and are just polluting the global namespace with a very generic name that other people already are most certainly using.

Even if in the end the Promise object makes it in to ES6, which would be great, what happens if something in the spec changes between now and then? Since the current version is already shipped, unprefixed, enabled by default developers will have to change their code because Chrome implemented a non-finalized specification and released it as if it was finalized.

Domenic Denicola • 10 years ago

You may wish to read the TC39 minutes, or at the very least the rest of the comments thread, before making such bold proclamations. Promises are most definitely an official feature of ES6; they have been accepted; the spec has been written; etc. They have not been incorporated into Allen's Word document draft, but that is also true of several other accepted-for-ES6 features, like the module loader.

The spec is stable, as it reached consensus at the September TC39 meeting in Boston.

In general, we know what we're doing, and it's a little hurtful for you to so brazenly assume otherwise. Calm down. We've got this.

(I agree that the Chrome status dashboard is unnecessarily confusing. So I emailed them and got it fixed.)

Domenic Denicola • 10 years ago

It seems that you didn't read the article?

Guest • 10 years ago

IMO this article could be much better written. I couldn't even read it entirely :-(.

The below video is more interesting than this waste of "paper".

Forbes Lindesay: Promises and Generators -> http://www.youtube.com/watc...

jaffathecake • 10 years ago

In terms of constructive criticism, what about the article did you find wasteful & difficult to get through?