I think I understand what Promises are about, and I think I understand what continuations are about, but I still fail to see what their connection is. In what ways do Promises use Continuations. They never return, which is explained by the fact that they use Continuations, but I don't see when and why they do the "jump" which is so characteristic when working with Continuations.
The code that is executed after a synchronous call to a function becomes the continuation that you pass to a promise when you call the same function asynchronously.
To give you some intuition, here is how a piece of synchronous code is transformed to a piece of asynchronous code. The whole answer is written informally, with no particular programming language in mind.
actionsBeforeCall(); x = computeSomething(); actionsAfterCall(x);
This illustrates schematically, that there is some code before your call, then the call which can return a value x, and then some other code that may depend on the result of the call through variable x. Of course there may be more than one variable (
y, etc) or no variable at all but the actions after the call depend on some global state that has been left behind by computeSomething().
If you want to execute the call asynchronously, you can create a promise:
actionsBeforeCall(); p = promise(computeSomething);
A promise is an object that represents a computation that will complete some time in the future, so the thread that started the asynchronous computation can still have a handle to communicate with it. Now the main thread will not stop waiting for the asynchronous function to terminate, because it is running asynchronously. So we have two problems:
- How does the main thread tell the asynchronous computation what to do when it completes?
- How does the asynchronous computation return its result: it is now decoupled from the thread that called it and it cannot return any value.
A continuation solves both problems:
- It is a function that must be invoked when the asynchronous computation completes.
- The continuation takes an argument
xthat represents the result returned by
computeSomething. Alternatively, it accesses some global variables to fetch the result produced by
So in the main thread you write
actionsBeforeCall(); p = promise(computeSomething); p.onComplete(actionsAfterCall)
The last line means: when
computeSomething is finished, call the function
actionsAfterCall with the result. In turn,
actionsAfterCall must take an input argument, so that the promise can internally do something like:
x = computeSomething() ... actionsAfterCall(x)
An important point is that in asynchronous computations you do not return result values. Instead, you pass the result of a computation to a continuation.