10 ms·
How do Promises Work?
- spooningtamarin 11y agoThey abide by the beautiful monadic laws.
- jmcomets 11y agoWhen the event driven programming / promise hype started, I was like `Maybe $ exists thisConcept`
- radiospiel 11y agoNicely done, thanks! Still, having to explain a concept as simple as eventual computation reinforces my belief that promises as a whole is broken, and should be done in something that looks like synchronous code with some help of the underlying runtime. (And no, ES7's async is not good enough for me here)
- Shog9 11y agoSounds like a leaky abstraction. Easier to get started with maybe, but woe betide the legion of programmers who make assumptions based on the appearance of synchronous execution only to be left stranded when those assumptions don't hold. Asynchronous execution isn't an unfortunate design choice to be papered over; better to make it as easy as possible to learn and work with.
- radiospiel 11y agoI am not calling to ignoring the async nature of computing in general, I am calling for better tooling. The Linux (and other OS'es) kernel handles I/O asynchronously, yet processes using open(2) + friends work quite well and are usually written in synchronous style. That the actual asyncness is hidden in kernel space does in general not lead to bad programs; and whoever needs async in the space of a single process can still use async tools provided by the kernel when needed. That tooling is much better as it leads to simpler code. I don't see ES7's async support being there yet. Elixir, Go, a number of others: much more.
- Shog9 11y agoApples and oranges. You're describing a platform where it's considered acceptable for programs to block on IO and jump through hoops in rare cases where that's absolutely not ok. A set of assumptions that maybe still made sense 30 years ago, and also a leaky abstraction, but one we've become accustom to working around. If you want that in JavaScript, you can have it; promises were created on the assumption that asynchronous logic is desirable as a matter of course.
- anonymoushn 11y agoWhat are you going to do in Javascript, though, if the I/O takes some time to happen? In golang or lua or whatever I can say "this execution context should block on the I/O for x seconds, then give up and return an error." (the other tens of thousands of execution contexts can keep going) In Javascript I would probably do... the same thing? But I would do it using promises?
- Osiris 11y agoSome Promise library add support for things like `.timeout` so you can force a promise to reject if the promise takes too long to resolve. In cases like a node.js server, Promises allow a single node instance to handle thousands of concurrent requests because the event loop isn't blocking waiting for a single IO request to complete, the process can happily do lots of other things while Promises are pending.
- anonymoushn 11y agoYes, I expect Promise libraries to support timeouts. So it's right that promises allow me to do the same thing I would do with sequential code in other languages? If I had the option of using coroutines when would I choose to use promises? Edit: I'm asking because the context of this thread is that one person said that sequential APIs for asynchronous operations, such as open(2), are pretty nice, and someone else said no they're not pretty nice and we should explicitly deal with the asynchronous nature of operations like open(2) by NOT writing sequential code there.
- findjashua 11y agoi love elixir actors / golang goroutines, since i don't have to give any special treatment to async operations.
- chenglou 11y agoDoes elixir solve this problem? http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- anonymoushn 11y agoCan you clarify? In golang, by convention, you only ever use asynchronous functions that return values. So there is "only one color." (You also have the option of writing callback spaghetti or implementing promises, but why would you do that?) In elixir, you have the option of blocking on a Task. So you can do the same thing you do in golang, if you want. I don't know enough about elixir to say what the culture is like around this.
- fzzzy 11y agoYes. In elixir and erlang, you can call a function without knowing whether it will wait on an asynchronous message. The function you called will not return until the message is received or a timeout is reached.
- Xixi 11y agoIt does. The problem is simply solved because Elixir has preemptive multitasking [1], and supports millions of processes on a simple server. So let say you have a blocking function fetch_data_sync, and want to execute it in a non blocking way: just spawn a process: task = Task.async(fn -> fetch_data_sync() end) do_some_other_work() Task.await(task) |> do_something_with_data() So you can mix 'red' and 'blue' functions as much as you want. Problem solved. [1] Erlang/Elixir multitasking is often called preemptive, but in fact it is a little bit more subtle than that. It is however a good first approximation when writing code.
- 11y ago
- ashark 11y agoNodeJS: you needed 5% of your code to be async, so we made everything async so you get to write awkward async-but-not-really code for the other 95%, too. That's better, right? At least that's how it's always felt when I've used it for anything non-trivial yet well within its typical set of use cases.
- taurath 11y agoIn a typical web API in node that I've seen, by function count I'd say around 60-70% of code is asynchronous.
- ashark 11y agoWell yeah, I usually end up writing lots of async code because you pretty much have to since all the libraries assume that's what you want, but shoehorning in dependencies (promises, callbacks, callbacks mutated into promises by a promises library—it's a mess) until it could have just as well been written as at most two threads. So it's async in pattern but not in fact.
- taurath 11y agoMost of the time that IS what you want in general in node, as if you start running anything with even moderate CPU time (let alone a call to an external service) you block the event loop and chug the system. The big performance gain you get with node in the form of max simultaneous connections per clock you get from not blocking. Most (95%+) of packages I've seen use callbacks, and maybe ~10% expose promise interfaces even if they use them internally.
- dboreham 11y agoDespite the downvotes, you are absolutely correct. My feeling, after working on a moderately complex server application in Node is that it was designed for only very simple applications that can be done with one level of callback. I find the "it works this way because its better" cult quite irritating. It works that way because the #*(^#$&^ JS interpreter isn't multithreaded.
- davexunit 11y agoWhat JavaScript needs (and other languages need, too) are first-class continuations. First-class delimited continuations to be precise. If you have those, you can implement whatever control flow you'd like, such as coroutines.
- anowlcalledjosh 11y agoCame expecting psychology, was disappointed.
- deleted 11y ago[deleted]
- hvmonk 11y agoI have seen how Promises' concept was abused in a project at work. All the promises were just returning Future objects, and they were exposed everywhere. And, in case a future fails for some reason, there was no way to have a new Future: all users were doing future.get, resulting in an exception thrown to the caller. What a mess.
- seivan 11y agoHmm, I never got into using Promises. Jumped directly into Rx* and Observables.
- untog 11y ago...okay?
- runn1ng 11y agoWhat is the difference between "Promise" and "Future"? From what I read, it is basically the same thing.
- nanodeath 11y agoAs far as I can tell, there's two differences. 1) a Promise typically has a "public" API and a "private" API -- the private API has read/write access and the public API has only read access. This way you can return the public API object to consumers of a method/library without worrying about them mutating the promise. 2) Promises tend to be more...composable? Java's "CompletableFuture" API fails on the #1 point I made above, but otherwise has a whole slew of then* methods that aren't on Java's Future. Then again, that might just be because Java's Future is mind-numbingly simplistic.
- the_af 11y agoThen again, Scala's Future/Promise terminology seems to be something else, though of course related.
- 11y ago
- elchief 11y agoStupid Cisco blocked this site on my corp network, due to "pornography", assuming due to "lolita" in URL.
- sotojuan 11y agoI can't wait for async/await enough myself.
- ilaksh 11y agoJust use babel.
- wonnage 11y ago> Another way of solving this problem comes from the realisation that we only really need to keep track of the dependencies for a promise while the promise is in the pending state, because once a promise is fulfilled we can just execute the function right away! This is a common gotcha in Javascript implementations, in that you think you want this, but you really don't! Now you never know if your code is synchronous or will run in a subsequent tick. Your call tree will look completely different depending on race conditions... This comes up in user code as well; any time you write a function that takes a callback, it's probably a good idea to either always run it either in the same call tree or in a new stack, but never mix the two. It's usually easier to just do the latter using process.nextTick.
- tlrobinson 11y agoPromises also solve this problem. If you immediately resolve a promise it isn't actually resolved until the next event loop "tick": new Promise(resolve => resolve("resolved!")).then(result => console.log(result)); console.log("next statement"); Prints "next statement" then "resolved!".
- jallardice 11y agoYou don't even have to resolve the promise via a callback for that to be the case - you can use the static resolve method. This is really useful if you have a function that can sometimes return a promise and sometimes a "normal" value. Promise.resolve("resolved!").then(result => console.log(result)); console.log("next statement");
- mistercow 11y agoIt's actually not the next event loop tick (or it's not supposed to be; some implementations get this wrong). Promises use microtasks, so an already-resolved promise dispatches at the end of the current tick.
- erikpukinskis 11y agoI get what you're saying, but for some reason it doesn't feel right to me. Part of it is that if your API requires you to think about whether or not it will run in the current or next tick, that's probably not the right API. I find in JavaScript it's best to just assume your callbacks could be called at any time, in any order. And if you need a specific priority then write some code that actually explicitly orchestrates that.
- hotBacteria 11y agoI found this document : https://github.com/kriskowal/q/tree/v1/design https://github.com/kriskowal/q/tree/v1/design very helpful to understand how promises work behind the scene Also liked being able to access the author's reasoning and the motivations behind his design decisions
- inglor 11y agoIf you're going to mention Kris and concurrency, you better mention: https://github.com/kriskowal/gtor https://github.com/kriskowal/gtor
- rdtsc 11y agoSee section about handling error and promises. Very well done! You won't see that in the typical "check out how cool promises are, async and fast all the things". And promises indeed look cool, and make for nice short demos and they are easy to understand in short examples. Only when you start building a large applications based on them, where error handling has to be done, you start realizing they are bit like threads. Promise callback chains started from one event, can interfere with other promise callback chains that started from another event. And if they modify the same data, you now also have a race condition as well. I would basically look at this statement "In the synchronous world, it’s very simple to understand computations when thinking about functions: you put things into a function, and the function gives you something in return" and follow through, but in a different direction -- pick the synchronous world if you can. Sequential things should be sequential and concurrent things should be concurrent. Single request processing is sequential. For this request do x,y,z in order. Can't do y unless x finished. Well sit and wait for x to finish. But requests themselves can be concurrent and run in parallel. For example a request comes in, reads the database, updates the database, bumps metrics, makess other sub-requests and then responds. It is sequential and should be kept sequential. If you platform cannot handle that, think very well about your platform, and perhaps pick a better platform. What does that mean practically? It means picking green threads (Python gevent, eventlet), it means Elixir, Erlang, Go goroutines, Rust's threads. Streams can be used as a higher level abstraction sometimes and so on.
- Rusky 11y agoRust no longer has green threads. Another good way to make things sequential is C#-style async/await, which is enough syntactic sugar over promises that everything looks sequential again.
- rdtsc 11y agoIt has regular threads but with appropriate data race safety guarantees. So that won't help in context when large number of concurrent contexts are happening, but perhaps it should first become a bottleneck and then solved, because it might just be good enough.
- 11y ago
- inglor 11y agoAnother good classic about it is Domenic's you're missing the point of promises at https://gist.github.com/domenic/3889970 https://gist.github.com/domenic/3889970 from 2012
- debacle 11y agoMaybe JavaScript isn't the best language to explain Promises in considering how verbose they seem to be next to procedural code.
- benjaminjt 11y agoNice summary! I wish I had this when I started getting into Promises a couple of months ago. For a really interesting look ahead to how Promises (and more!) might address sync/async symmetry in ES6/7, check this out: https://youtu.be/DqMFX91ToLw https://youtu.be/DqMFX91ToLw
- pjzedalis 11y agoThis article has put me over the ledge of taking some time to really study Go. Goroutines are starting to sound very attractive.
- ilaksh 11y agoReally a lot more explanation than necessary. Get a feel by using them and using a debugger or just console.log to trace execution. Then compare with equivalent callback code and async/await with babel. Then use async/await and look at node-modules.com to find modules that convert to promises so you can use async/await.
- taternuts 11y agoTIL: fulfil vs fulfill [1] 1. http://grammarist.com/spelling/fulfil-fulfill/ http://grammarist.com/spelling/fulfil-fulfill/
- bhrgunatha 11y agoThe second nGram linked there (showing the rise of fulfill - with 2 l's in American English)[1] seems to show that the increase in use of 2 l's started around the same time as Noah Webster's dictionary was first published. [2] I've always been fascinated why there is such a difference between spelling in "British" and "American" English, I remember reading that the main reason was Webster's dictionary and his desire to reform spelling according to pronunciation. That Wikipedia article though quotes John Algeo: "He was very influential in popularizing certain spellings in America, but he did not originate them." So now I'm as confused as ever. Why DID the spellings change so significantly. I can accept that they took hold due to Webster's work, but I wonder why the differences arose in the first place? [1] https://books.google.com/ngrams/graph?content=fulfil%2Cfulfill&year_start=1800&year_end=2000&corpus=5&smoothing=3&direct_url=t1%3B%2Cfulfil%3B%2Cc0%3B.t1%3B%2Cfulfill%3B%2Cc0 https://books.google.com/ngrams/graph?content=fulfil%2Cfulfi... [2] https://en.wikipedia.org/wiki/Noah_Webster#Dictionary https://en.wikipedia.org/wiki/Noah_Webster#Dictionary
- hosh 11y agoAnd here I thought someone besides Mark Burgess was writing on Promise Theory...