I don't think so. Here's a concrete example of what I mean.
Say we are making a Todo list app. We have a list of Pending and a list of Completed todos, and when the user completes one, we move it from Pending to Completed. But how do we ensure other threads can't see the transient in-between state? Traditional concurrent programming might solve this with a lock:
This problem is very well known, and any discussion of threads will spend a lot of time on locks, queues, serialization techniques, etc. for avoiding races. Now, with futures:
Pending.remove(todo).then({Completed.add(todo)})
We've got the analogous race condition, even if we're single threaded. But articles on Futures never seem to discuss techniques for mitigating this. Why not? Is there a Futures equivalent for a lock?
Futures are used when you want to do something asynchronously -- that is, you want the action to happen in the background and be notified after it completes. If you want actions to take place atomically, then why make them asynchronous? Nobody ever said that all code blocks of the form "A; B" should be replaced with "A.then(B)".
If you're writing in C++ with shared data structures accessible from multiple threads, you need locks anyway; futures don't change that. If you're writing in Javascript, your code is inherently single-threaded and no locks are necessary.
> 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?
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.
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.
Say we are making a Todo list app. We have a list of Pending and a list of Completed todos, and when the user completes one, we move it from Pending to Completed. But how do we ensure other threads can't see the transient in-between state? Traditional concurrent programming might solve this with a lock:
This problem is very well known, and any discussion of threads will spend a lot of time on locks, queues, serialization techniques, etc. for avoiding races. Now, with futures: We've got the analogous race condition, even if we're single threaded. But articles on Futures never seem to discuss techniques for mitigating this. Why not? Is there a Futures equivalent for a lock?