35 ms·
What's so great about JavaScript Promises?
- ef4 14y agoPromises are not great -- I'd much rather have real coroutine support. But given that we don't have real coroutine support, promises are absolutely essential do writing sane, fault-tolerant Javascript.
- bguthrie 14y agoAgreed. Please, please use and return these in preference to callbacks.
- fernandezpablo 14y agocan you elaborate on what is "real coroutine support" and how are these better than promises?
- embwbam 14y agoImagine if the language handled async code for you. So you could call an async function and it "returned" a value. But it still uses the event loop. See http://taskjs.org/ http://taskjs.org/ for how it could work in ES6.
- ufo 14y agoAn analogy I like for the "language support" part is Python generators. All you need to do to turn a regular function into an async one is put the `yield` keyword where appropriate. Promises, are less worse then continuation passing style but they still require you to rewrite all your code using the promise library instead of reusing existing language constructs. If you want to carry the analogy even further generators can also implement coroutine patterns, if you use yield as an expression (for communication) and yield from to do nested generators.
- rektide 14y agoPromises make a handy wrapper for any kind of value-in-the-future, be that the mundane case of invoking non-blocking functions with callbacks, in the case of getting values from remote remote systems (https://github.com/kriskowal/q-connection https://github.com/kriskowal/q-connection), and, I dare say, if we had coroutines, they'd make a great first-class object for pulling result values out of them too. A promise is agnostic to your concern. Thanks for writing.
- munificent 14y agoIf you have coroutines, I don't think promises/futures are worth much. You don't need a value that represents "something I don't have yet" because an API can just block the current coroutine until it has the value. There may be some utility in being able to pass them around so that the code that calls the API isn't the place where the block occurs, but I'm not convinced it's worth the extra complexity and API fragmentation to care about that.
- jrochkind1 14y agoI'm curious what language you have used coroutines in where it works out that way. In my experience of using fibers in ruby (which I _think_ are substantially/exactly the same as coroutines?), it hasn't been like you describe at all -- promises, or something other higher level abstraction, are still pretty neccesary to do useful and comprehensible things with them. So my experience colors my understanding and I think "What's he talking about, that doesn't make any sense." More likely, you have experience in a language where things are done differently enough that it all comes out different. What language?
- shardling 14y agoIf for some reason you end up writing javascript that only needs to run on Firefox, you do get coroutines via the yield keyword. Pretty sure it will also appear in a future version of javascript. https://developer.mozilla.org/en-US/docs/JavaScript/New_in_JavaScript/1.7 https://developer.mozilla.org/en-US/docs/JavaScript/New_in_J...
- willvarfar 14y agoAh the lament that Chrome doesn't do yield, list comprehensions etc. Why, JS 1.7 looks almost like Python!
- Sephr 14y agoI wrote a library that adds support for this using yield: http://eligrey.com/blog/post/pausing-javascript-with-async-js http://eligrey.com/blog/post/pausing-javascript-with-async-j...
- ilaksh 14y agoLiveScript backcalls.
- deleted 14y ago[deleted]
- leeoniya 14y agopromises are also pretty essential when doing RPC-esque back-and-forth with web-workers when you need to distribute a workload and wait for all results to come back to continue a chain of ops on the main thread.
- davewasmer 14y agoSo, as far as I can tell, there are two benefits this article outlines: avoiding highly nested code, and handling certain types of errors better. Are there other benefits? In my experience, promises can be more difficult to debug (once the promise library takes my callback, I can't follow the flow of execution until it is called, unless I crack open the library itself), and are less intuitive. While those shouldn't disqualify the idea immediately, it does make me hesitate. And I'm not sold on the benefits to error handling either. In the author's example, yes, all those error handlers could be grouped together - but how often do you have that many async function calls with identical error handlers? In most situations, that is a warning sign that you aren't handling errors properly and with enough "resolution". All that said, I don't think promises are useless. There may be times when it makes sense to use them. But calling them the "next great paradigm in JavaScript programming" seems like a bit of a stretch to me.
- rektide 14y agoThe article immediately dives into what promises actually are, and you've done excellently at summarizing possible benefits from the examples listed. I would advise most strongly against this exercise in extrapolation: it is not a productive tasks to introduce a new technology via use cases without establishing some grounds for what the technology's role is. You've attempted to define, after reading, what that role is, and that to me bespeaks a horrible exercise that will draw bad results, and it speaks to a poor likely fruitless introduction for people who don't already know. The net is this: a promise is a value you have, can pass around, can chain more promisary stages of computation on to, or combine with other promises that describes something of the future. You have a symbol for the future that you can continue to operate on. That is the value of promises: it's just an object. There are no other JS constructs we have for a first-class future: the promise is the sum of taking as much away from asynchronous as we can, for boiling the asynchronous future down into a mundane, primitive value. It's surpassing notability is that a promise is just an object, unlike everything else we have for reasoning about the future. In callback style, one has to know ahead of time what one is doing in the future and have a function fully specified to deal with that. I see promises are fairly close to event style programming using only .once handlers, but the libraries for promises also focus on tasks like combining promises (Q.all, Q.allResolved) and there is chainability and error handling inbuilt: these sum to making Promises far easier to use in practice than I have events.
- kogir 14y agoPromises are fine, but I much prefer C#'s await. The last example using await: try { await Parse.User.logIn("user", "pass"); var results = await query.find(); await results[0].save(new { key = value }); } catch (Exception e) { // Some error handling } // the object was saved Does anything exist to do this in JavaScript? Can CoffeeScript emulate it? C# does it entirely in the compiler, I believe.
- bklimt 14y agoThere is a fork of CoffeeScript called IcedCoffeeScript that adds something similar: http://maxtaco.github.com/coffee-script/ http://maxtaco.github.com/coffee-script/
- rektide 14y agoThere's an extreme amount of magic going under the hood when you do this- from http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx http://msdn.microsoft.com/en-us/library/vstudio/hh156528.asp... : | "An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off." The implementation detail is that your caller's state needs to somehow be re-asserted when the asynchronous wait finishes evaluating- anyone mildly versed in computer hardware knows that to, in most situations (some green threading environments aside) be a very onerous scary intensive thing, speak nothing of the tasks of capturing and storing the continuation, which likely is fairly deep in a call stack somewhere. It's great magic, it's certainly putting a lot of magic programmers like to use at their fingertips. But be advised that it is somewhat scary, handing out magic wands like candy to newcomers and telling them it's OK and good.
- kogir 14y agoIt's only magic if it's poorly or non-documented. I'm aware of many of the internals, and have written my own SynchronizationContext before. Execution actually can (and often) does continue using the same thread. I still strongly believe it's better to make cool things available rather than try and protect people from themselves.
- MatthewPhillips 14y agoPromises do 2 things: 1) Give the illusion that control has been returned to the caller. It hasn't, but it kinda looks like it. This is soothing for people who don't really like JavaScript all that much. 2) Provide a structured way of dealing with callback-oriented code. You don't have to think about in a function whether the callback should be that last parameter, the only parameter. You don't have to think about whether it should include an errback or if the error should instead be the first parameter of the callback. You just do it the way the promises library describes, so the interface is predictable. #1 is of no concern to me. #2 I feel pretty meh about. A consistent interface is nice, but I don't lose sleep over a library using callbacks in a slightly different way than I like, I just write an extra function or two to wrap it into the way I like and move on with my life. I don't have a problem with Promises per se, I just don't want a library I'm using to pick a winner and tact on an extra few k. Doesn't look like ES6 is going to pick a promises winner, so I probably will largely ignore it until ES7.
- rektide 14y agoYour casual ambivalence for #1 makes me think you don't appreciate the greatest value of promises: that they are values, objects. Returning control to the caller is half the story. Along with returning control- which indeed is what is happening, no illusion, if the promise is triggering a non-blocking routine- the promise itself is returned: that object is what makes promises great. We have no other object for an execution or process in JavaScript: a promise, the value it returns, is an object. Objects can be used and talked about inside JS. If there is no object, there's nothing to compute about. A promise is a first class execution that we can compute about.
- im3w1l 14y agoWhat about timeout ids? I think I am missing the point.
- jasondenizac 14y agotimeout IDs are similar in a sense. By having the promise as an object, you can do neat things to modify program control flow as a separate concern from resolving a particular asynchronous operation. Sure, you can compose promises, just like you might with callback-oriented code. But you can also do neat things like await an object graph of mixed synchronous and promised values, eg: https://npmjs.org/package/resolved https://npmjs.org/package/resolved
- iamwil 14y agoFunnily, I was just doing some comparison shopping between different promise libraries as well. Besides avoiding highly nested code, and handling certain types of errors better, I've found that they also provide an optional progress callback, in addition to the success and error callbacks in then(). This is highly useful in reporting the progress of a long running process, to help give the user some update or stream the results as it's being completed. That cuts down on both the actual and perceived response time. In normal callback-style, it's awkward to have two callbacks, one for success/err, and one for progress. In addition, it's not the canonical form for node.js, so we often skip doing it. As for between the different libs, I've found that Q has one of the better APIs, but it's also the slowest. https://github.com/kriskowal/q/issues/153 https://github.com/kriskowal/q/issues/153 But that's because it uses Object.freeze(), which the js V8 penalizes with a slowdown, when iterating over it. http://stackoverflow.com/questions/8435080/any-performance-benefit-to-locking-down-javascript-objects http://stackoverflow.com/questions/8435080/any-performance-b...
- tlrobinson 14y agoThe usage of Object.freeze() was removed recently and is slated for release in v0.9 https://github.com/kriskowal/q/commit/ef6c1b9ce35c7bf3949c7035b877d153af766987 https://github.com/kriskowal/q/commit/ef6c1b9ce35c7bf3949c70...
- iamwil 14y agoAwesome, looking forward to the update, and speedup.
- grannyg00se 14y ago>That’s getting pretty ridiculous...but because of the way promise chaining works, the code can now be much flatter. In what way is the indented code ridiculous, and why is flatter better? Conceptually, you still have to think of the callbacks operating the same way in order to properly understand the code. Flattening it out and changing the shape so that it isn't nested doesn't really help change what is going on. Maybe I'm missing the bigger picture.
- rektide 14y agoIndenting is a symptom of having to write code structurally, having to tightly couple call side with it's handler. Promises break that structural linkage: we have the only thing in JS that matters, an object, a first class plain old object, that we can reason with and perform operations on as if it were any other object. It happens to be an object about a future which will one day resolve, but it's just an object. And because it's an object that can, after it's inception, have handlers added to it, we break the necessary structural linkage that callbacks mandate: we can make callbacks latter, we can condition what callbacks to attach, we can compose or chain more promises after our first promise and put our handler on that... we have a first class thing to work with, not just a function, which expects a fully-readied handler. It defers the need to define handling. Read more of my comments to get a better sense of why A) I adore promises B) I feel this article is injurous to the case for promises.
- jessaustin 14y agothis article is injurous to the case for promises. Haha I thought the same about the Crockford talk. Can you point me to a non-injurious source?
- jasondenizac 14y agosee also: https://gist.github.com/3889970 https://gist.github.com/3889970
- steveklabnik 14y agoIt might not matter to you with two levels, but once you get to three levels of nesting, I bet it'd be more clear how much nicer the flat version is. > you still have to think of the callbacks operating the same way in order to properly understand the code. It depends on your conception. ;) Maybe it's my Haskell/monad experience, but to me, there's a difference between "here's the control flow between these bits of computation" and "where does this level of nesting end, again?"
- shtylman 14y agoIf your original callback approach just had one callback it wouldn't look so terrible. success: error: is some old jqueryism.
- kikibobo69 14y agoWhy are these called promises and not futures?
- dotborg 14y agoES6 generators are futures
- tlrobinson 14y agoI'm glad promises are gaining traction in JavaScript, but it's a shame they chose to use jQuery promises rather than CommonJS Promises/A. The differences in error handling are significant. See Domenic Denicola's comment at the bottom of the article, and his excellent "You're Missing the Point of Promises" post: https://gist.github.com/3889970 https://gist.github.com/3889970
- nickporter 14y agoI think I'm the only one in this world who doesn't mind the async pyramid. To me, it is very intuitive. Also, if your pyramid is too deep, chances are you need to extract some of it into functions.
- jakejake 14y agoI don't mind the async callback structure either, but I have to admit having only one error handler would clean up some code for me.
- ufo 14y agoI hate the async pyramid because explicit continuation passing ought to be a low-level representation generated exclusively by machines, instead of by programmers. For example, one of the great things about high level programming languages is that you can write composable expressions such as f(g() + h()) but you can't do that if f and g are written in CPS style, since then you need to give an explicit names to all the intermediate "registers" used in the computation as well as baking down an specific order of evaluation for things g(function(a){ return h(function(b){ return f(a + b) }) }) > Also, if your pyramid is too deep, chances are you need to extract some of it into functions. This sounds like that "if your method is more then 15 lines you should split it into subfunctions". I never liked this line of reasoning because the complexity (number of possible interactions) in a module increases quadratically with the number of functions in it. You fight this complexity by only breaking down a function on "natural" boundaries dictated by encapsulation and reuse, rather then by code size.
- ilaksh 14y agoI think Iced CoffeeScript's await and defer are nicer. There is also LiveScript which has backcalls: x <- map _, [1 to 3] x * 2 # => [2, 4, 6] data <-! $.get 'ajaxtest' $ \.result .html data processed <-! $.get 'ajaxprocess', data $ \.result .append processed I think the LiveScript syntax is awesome.
- kzahel 14y agoMost of the time I'm processing data that is on a queue, and I have some kind of event loop. Promises are great for simple things, I guess. But they just seem like extra, unnecessary syntax. I tried a while ago looking at the different libraries to see if they would give me a "resolveAll/sequential/parallel" for a list of promises, but none of the libraries even seemed to do something very basic like that, which is where I would get any actual utility. I guess most promises are told to "execute" (do their asynchronous thing) as soon as the promise is created. I'm more interested in being able to have more fine grained control about when the execution happens, too. All in all, I find promises to be pretty pointless in javascript. Until ES6, that is. I also experimented with the "yield" expression in google's traceur compiler, but they were still working out their issues last time I tried.
- Twisol 14y agoPromises actually aren't far off from functional reactive programming (FRP). The idea in common is to take the abstract notion of an entry point and make it an actual object to be manipulated.