How do Promises Work?
robotlolita.me
robotlolita.me
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.
new Promise(resolve => resolve("resolved!")).then(result => console.log(result));
console.log("next statement");
Prints "next statement" then "resolved!". Promise.resolve("resolved!").then(result => console.log(result));
console.log("next statement");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.
Here's a good article about how callbacks get scheduled: https://jakearchibald.com/2015/tasks-microtasks-queues-and-s...
No, I really, really, DO want this. And I want my entire codebase written in such a way that this is fine.
You need consistent abstractions to make asynchronous logic flow comprehensible. And once you have them, stick to them rigorously. Promises are such an abstraction. Build on it, don't ruin it.
Yes, there are lots of ways to ruin it. And for every way it can be ruined, there is a way to make it work without breaking the abstraction. Do the latter.
That's still asynchronous though isn't it? (Correct me if I'm wrong I'm just spitballing, still learning).
There's no lower bound on the minimum amount of ticks a process can take for it to be async. What gives it that async-ness is that the upper bound is unknown, indeterminate, and changeable.
Ergo, if some process is sometimes synchronous, and sometimes asynchronous, it's really a process that is asynchronous, but just so happens to occasionally execute in synchronous-like time.
Of course, that would be silly, since Node manages an event loop for you. So your nonblocking IO would instead push events onto the queue, and let Node move on to the next tick. If something happens in a different tick, it gets its own clean slate of an execution context, and we call this "async", since the function that triggered it is no longer running.
The promises you set up should always be assumed to do things in whatever order they happen to happen. Your code that does interesting stuff happens before any promises are seen. The interesting code in your returned promise will happen after the necessary promises are resolved.
As long as things are always set up this way, you'll be fine no matter how the promise library is set up. Which is good because unexpected implicit dependencies are a problem.
This doesn't break the abstraction, but it does make the abstraction simpler. It means that your callbacks (or whatever abstraction is built on top of them) are always called from the same context.
From what I read, it is basically the same thing.
The difference is SUPPOSED to be that a Future is read-only while a Promise is settable. And should be set at most once! So you can use a Promise as a Future, but you can't always use a Future as a Promise.
Linguistically you can understand this as, "I Promise you'll see a Future answer." I need to be able to write to the Promise to fulfill it. You can only hope that some day the Future will arrive.
The ideas are much older than Scala, JavaScript, and many of the people commenting in this discussion. Believe it or not, both terms date back to the late 1970s.
Scala got it right. JavaScript got it wrong. And polyglots like me have to suffer with having to sort out who means what, where, on what platform.
on JVM: Future is read-only (public), Promise is writable (private)
The writable interface has "resolve" and "reject" to return a value. The readable interface has "then", "error", "finally" etc which let you compose and combine functions that deal in asynchronous values.
The best Javascript implementation of promises is Bluebird, which doesn't expose a Deferred instance to avoid confusion, it is very nice. bluebirdjs.com/docs/api-reference.html
When you chain and compose together operations on promises using a nice fluent API, you're building up a monadic value that encapsulates the whole computation. You can then apply error handling to the final monadic value and be sure all errors across the whole computation will route to a single handler. When reading and writing code, the division between "creating the computation" and "running the computation" is thereby explicit and the error handling obvious.
I've found promises most useful in managing asynchronous operations in the browser. With UI widget support (disabling things for the duration of a promise computation, showing spinners, showing progress bars, etc., automatically handling errors in an error alert area) it makes non-blocking UI much much easier to get right. For parallelism, I find different approaches easier to reason about, whether it's work queues or something like parallel LINQ / Java 8 Streams.
i am a mediocre developer and it is super hard to find resources. either they are way to beginner such as an introduction yo for loops, a project structure that is not modular but one monolithic binary for the basic example or an otherwise simplified usecase that can not really be extended very well.
otoh, some stuff is way to intense and can't grep that either. if you know of any good design patterns that explain concepts like promises, leveraging functional closures in a factory/singleton or otherwise great code concepts; but explained either at the intermediate or beginner level, that would be great.
i love resources that assume limited knowledge, they just don't seem to go into the interesting/useful parts of the language in enough depth to leverage
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.
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.
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.
In Javascript I would probably do... the same thing? But I would do it using promises?
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.
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.
If you don't have either, then you'll probably end up re-implementing one or the other eventually anyway.
// using Promise.race so whoever finishes first wins
var fetchingWithTimeout = Promise.race([
// first promise: actually try to perform the computation
fetching,
// second promise: wait x seconds, then reject
Promise.delay(x*1000).then(Promise.reject('Timed out')),
]);
You can apply the timeout wherever you want (or apply different timeouts in different corners of the app) without throwing out the raw promise to perform the computation no matter the time cost.I personally prefer the async solution, and I think async/await is a good solution to make async code a little more readable.
What do you mean the leaky blocking IO. It is the non-blocking IO that is leaky, isn't it. If at the business logic there is a request being processed that needs to go through steps a,b,c and then return. b, can't be done unless a finishes and c unless b finishes is mapped pretty cleanly to an execution context of a thread/actor/goroutine/task/process.
Shoving IO callback functions in between handling the steps, or spreading them across callbacks or using promises is leaking the abstraction of the platform not being able to handle concurrent processing very well.
I agree that if you have some pure linear steps (do A, then do B, then do C, and probably use the result from the last step for the next), then the synchronous abstractions work fine. But as soon as you add various timeouts, different error handling strategies, multicast communication, cancellation and other stuff to your async IO procesing then it isn't purely linear anyway. I often end up implementing quite complex state machines for heavy IO related code, and for this I am more happy with working in an asynchronous single-threaded environment.
Creating a socket() creates a blocking socket by default, doesn't it. Doing a recv on it, blocks the current thread. Pretty sure by default IO is synchronous. Sure at some point they are all connected to a single wire plugged into the switch and there is a single network card. But if you are thinking that low, then well javascript is not the framework to talk about we are in firmware, dpdk, and driver land.
The socket API is synchronous. Do do stuff "asynchronously" you have to go the extra mile and enable turn on some options, use select/poll/epoll callbacks, explicitly pass state around between callbacks etc.
> What blocking does is adding an extra step (start the process AND waiting for the result) and thereby hiding something.
See to me at the socket layer non-blocking is the extra step. Having to enable a new mode, then use some extra polling hub to see which socket has data etc. That mixes context between separate requests, etc.
> I often end up implementing quite complex state machines for heavy IO related code
In specializes cases for performance reasons I think switching to asynchronous, but that is a rather special case. It works for short callback chains and situations optimized for IO throughput say things like nginx or haproxy. Doing it for an online store or some business middle layer would be very tricky.
Look at the LWIP library for embedded for example. There the async API is the default one, and you have to take the extra mile (and some performance hit) to get the blocking behavior. The midori OS prototype also seems to have implemented a pure async IO subsystem.
I also think it depends on the use-case whether one or the other model is more appealing, but I don't think it's only for performance reasons. A lot of people here think about IO mainly in terms of http request processing because web services are the usual domain here. For this such applications I think blocking abstractions come quite naturally, because you there you read the input stream once, then do a sequence of intermediate processing steps and then write a response. If you instead are working on other domains (writing a message broker, a gateway application, http/2 library, realtime sensordata processing system, ...) where there is no linear sequence of read/write operations and where you have shared state between your IO endpoints then your needs and the preferred building blocks might be different.
> for heavy IO related code, and for this I am more happy with working in an asynchronous single-threaded environment.
You're not describing the majority of applications. The vast majority of applications do negligable amounts of complex network I/O (ignoring GUI, which is handled for them by... an abstraction).
It would be a bad idea to base our abstractions on things only needed for a tiny minority of applications.
When I worked on simulation code some years ago I haven't thought about complex IO at all. Even when I did some basic web development (with PHP...) 16 years ago I had nothing but synchronous IO and was happy. Now realtime gateway systems are my daily business and the "typical application" looks completly different.
And you already have given GUI as a counter example yourself. More then a tiny majority of applications have a GUI. And all major GUI frameworks are built on top of asynchronous abstractions, because you want GUIs to be reactive (no hanging due to blocking stuff), and because every input port (either from a human input device or from network) can change the shared state and cause output to more than one output device.
I'm specifically going against my personal bias. I write server-side applications, but most of the code out there is basically apps and such.
> And you already have given GUI as a counter example yourself. More then a tiny majority of applications have a GUI. And all major GUI frameworks are built on top of asynchronous abstractions, because you want GUIs to be reactive (no hanging due to blocking stuff), and because every input port (either from a human input device or from network) can change the shared state and cause output to more than one output device.
If you look at how the users of GUI frameworks write their code, it's mostly synchronous code with some interspersed message-passing type code. The asynchronous nature of GUIs is only really relevant when dealing with button presses and such.
You don't want to have to program all of it as if it's asynchronous. Having to reify the stack manually into a state machine would quickly induce madness for all but the most trivial of GUIs.
Reactive GUIs are pretty simple to do using blocking code: Have a main thread resonsible for the GUI, offload long-lasting work to other threads. Use message passing for communication. Done. You're still programming in a mostly-synchronous model. I think my point stands.
EDIT: Btw, the beauty of this message-passing-mostly-synchronous model is that it doesn't force you to completely change everything in your program around the asynchronous bits. You restrict your asynchrony to where it really matters. That's a big deal for clarity, IMO.
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.
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.
The same is technically true of Go. In practice, in both cases, unless you're backing to a lot of C code, or in the case of Go, manage to write a really tight loop that never gives the scheduler a chance to run, it is not something that comes up in practice very often. (I'm at a total of 0 after ~6 years of use of Erlang and Go. YMMV, since I never did use any oddball C extensions, but every month for both languages that's less of a restriction than it used to be.)
Here's a simple test for checking this: does the language provide a green-thread/coroutine abstraction, or do you work at the OS thread level:
In JS, C#, JVM languages, Python etc you work at the OS thread, so async operations need to be handled differently from synchronous one.
Elixir/Erlang, Go, Haskell etc provide green thread abstractions, so your green thread can block on an async operation without blocking the OS thread.
Elixir/Erlang is especially nice since each process (green thread) is completely isolated. This has multiple advantages:
i) when an erlang process (green thread) crashes, it doesn't take the other green threads on the OS thread down with it.
ii) garbage collection is per erlang process, not for the entire run-time. So the garbage collector doesn't halt everything when it runs. This makes it a really solid option for soft real-time systems
iii) stack trace is also per green thread, so you only see execution steps for the particular green thread in the stack trace, this makes debugging much much easier
iv) erlang processes can communicate with processes on other machines, this makes it a really good option for dist systems
The only drawback is that it's slow for cpu bound stuff, so i'd avoid it for anything that's computation intensive
disclaimer: elixir fanboy
The platform is better designed and smart where you don't need red and blue function. You just have green functions (being a bit silly here with colors).
> [From Post] Async functions don’t compose in expressions because of the callbacks, have different error-handling, and can’t be used with try/catch or inside a lot of other control flow statements.
Good news again. Don't worry about that. Work sequentially as you need in inside each request/task. If you want to do multiple tasks in parallel, spawn multiple tasks. The Erlang folks (and Go-routine folks too but a bit in a different way) figured this out many years ago and have built large, complicated and reliable distributed systems (WhatsApp, smartphone<->internet gateways, databases etc...)
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.
(I'm sure there are potential problems with this, but hopefully it would be overall no worse than the current situation, while being easier to work with.)
on the node side https://www.npmjs.com/package/webworker-threads
I'm sure there's a workload where async-all-the-things makes things easier rather than harder, I just haven't run into it.
[EDIT] "rarely never" to the intended "rarely ever"
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%2Cfulfi...
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
Then use async/await and look at node-modules.com to find modules that convert to promises so you can use async/await.
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.
Another good way to make things sequential is C#-style async/await, which is enough syntactic sugar over promises that everything looks sequential again.