Promises are not great -- I'd much rather have real coroutine support. But given that we don't have real coroutine support, promises are absolutely essential do writing sane, fault-tolerant Javascript.
Promises make a handy wrapper for any kind of value-in-the-future, be that the mundane case of invoking non-blocking functions with callbacks, in the case of getting values from remote remote systems (https://github.com/kriskowal/q-connection), and, I dare say, if we had coroutines, they'd make a great first-class object for pulling result values out of them too.
A promise is agnostic to your concern. Thanks for writing.
If you have coroutines, I don't think promises/futures are worth much. You don't need a value that represents "something I don't have yet" because an API can just block the current coroutine until it has the value.
There may be some utility in being able to pass them around so that the code that calls the API isn't the place where the block occurs, but I'm not convinced it's worth the extra complexity and API fragmentation to care about that.
val resultA = something()
val resultB = somethingElse()
return resultA + resultB
With Futures, the processing for resultA and resultB would be done in parallel, because the call to something() does not block. To combine them you don't even need to wait for the processing to finish, because you just create another Future.
Also, being able to pass them around is a really important use-case and Futures are also all about error handling. With Scala it's really trivial to wrap the async support in Servlets 3.0 and make your controllers return Future responses. Then in the servlet you just attach onComplete and onFailure events to return the request when it's ready or when it fails. Works like a charm.
The greatest thing about Futures is that the concept is entirely agnostic to the underlying implementation details. You could have a Future result that's being processed asynchronously by an IO loop (e.g. ning's AsyncHttpClient), you could have a Future result that's being processed by a thread and you can combine the results.
If anything, I don't think you worked with Futures much. You should try it out in Scala, where Futures (from Akka or Scala 2.10) are modelled as monadic types, making them highly composable. Doing multi-threading with Futures in Scala is like working with Lego blocks.
I'm curious what language you have used coroutines in where it works out that way.
In my experience of using fibers in ruby (which I _think_ are substantially/exactly the same as coroutines?), it hasn't been like you describe at all -- promises, or something other higher level abstraction, are still pretty neccesary to do useful and comprehensible things with them. So my experience colors my understanding and I think "What's he talking about, that doesn't make any sense."
More likely, you have experience in a language where things are done differently enough that it all comes out different.
If for some reason you end up writing javascript that only needs to run on Firefox, you do get coroutines via the yield keyword. Pretty sure it will also appear in a future version of javascript.
An analogy I like for the "language support" part is Python generators. All you need to do to turn a regular function into an async one is put the `yield` keyword where appropriate. Promises, are less worse then continuation passing style but they still require you to rewrite all your code using the promise library instead of reusing existing language constructs.
If you want to carry the analogy even further generators can also implement coroutine patterns, if you use yield as an expression (for communication) and yield from to do nested generators.