Live data from Hacker News

How do Promises Work?

robotlolita.me

21–30 of 91 posts

Re: How do Promises Work?

#22
post #20
post #11

I 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.

What is the difference between "Promise" and "Future"? From what I read, it is basically the same thing.

As 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.

Re: How do Promises Work?

#24
> 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.

Re: How do Promises Work?

#25
post #9

Earlier quoted context omitted.

i love elixir actors / golang goroutines, since i don't have to give any special treatment to async operations.

Does elixir solve this problem? http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...

It 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.

Re: How do Promises Work?

#26

Nicely 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)

What 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.

Re: How do Promises Work?

#27
post #24

> 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 o…

Promises 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!".

Re: How do Promises Work?

#28
post #11

I 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.

IMO if you're to get the benefits of promises, you need to use callbacks rather than using synchronous accessors like a .get() method. Making a 'get' accessor available just encourages blocking, reducing the very parallelism and asynchrony that promises are introduced to make usable.

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.

Re: How do Promises Work?

#29
post #25
post #9

Earlier quoted context omitted.

Does elixir solve this problem? http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...

It 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…

"Erlang/Elixir multitasking is often called preemptive, but in fact it is a little bit more subtle than that."

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.)

Re: How do Promises Work?

#30
post #14
post #8

Earlier quoted context omitted.

NodeJS: 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.

In a typical web API in node that I've seen, by function count I'd say around 60-70% of code is asynchronous.

Well 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.
Post reply on HN