Hacker News new | ask | show | jobs
by millstone 4019 days ago
> If you want actions to take place atomically, then why make them asynchronous?

This is usually up to the API you are using. You may not have a choice.

> your code is inherently single-threaded and no locks are necessary

Locks are necessary in the single threaded case. See my example:

    Pending.remove(todo).then({Completed.add(todo)})
Nothing prevents another operation from executing between the remove() and add() calls, and seeing the transient state. You need the analog of a lock to prevent that. What is that with Futures?
2 comments

Sorry, maybe I was unclear. Let me try again from a different angle: It's up to an API designer to come up with a sane API, including sane usage of futures/promises only where it makes sense.

In the browser world, futures or promises are an abstraction over some operation that doesn't block the UI thread, and therefore can allow some other work to happen in the meantime. In your example, if Pending and Completed provide an interface to some remote API, then a lock provides no benefit because making separate RPCs can't possibly be atomic anyway. If they're operating on local data or the DOM, then making them return futures is pointless because the work will happen on the same thread, and no other code could possibly observe the intermediate state anyway. (This is a core principle of the browser event loop: Javascript code is never preempted, it can only yield control by running to completion. Apologies if I'm repeating stuff you already know.)

In other languages, futures are more flexible because they can contain CPU-bound work that operates on shared memory. In that case, futures don't magically absolve you of the need to protect that shared memory. But if you have multiple operations on the same shared data, it once again doesn't normally make sense to decouple them with futures in the first place.

That makes sense. Thank you for your thoughtful reply.
A future or promise is not a replacement for locks and mutexes. They are intended to abstract async operations for lazy evaluation and intend to fix the problem of "callback hell" that occurs when chaining async operations and response handlers.

Futures and promises make more sense in functional languages because they don't have a shared memory model, which avoids having to share resources between parallel tasks using locks. Google dataflow programming for more insight into how lazy evaluation and referential transparency parallelize work when there's no mutable state.