By definition, a computer program is a collection of instructions that are designed to perform a specific task. It is inherently important for a developer to understand each individual instruction or step the program has to take in order to reach its goal correctly. Naturally, it can be valuable to a developer to have more control over these instruction depending on the program at hand. When the program is executing some specific instruction, the collection of steps that still need to be processed in order for the program to complete a computation is known as a continuation. Some languages are able to provide its users this ability to treat continuations as first class features, while some other languages require its users to implement them manually. Regardless of the kind of program being created, continuations can always be a valuable concept and tool for a developer to understand in order to be equipped to build smart and sound programs.
Using continuations to your advantage within computer programs can yield powerful results. A common method for using continuations is known as continuation-passing style. In the continuation-passing style paradigm, control is within the program is passed explicitly by continuations. This is in direct contrast to more widely used styles of programming such as direct style. The direct style of programming is can generally be described as synchronous programming, in which continuations are passed implicitly by just advancing to the next line of the program. While some languages in particular have native support for continuations, such that continuations are treated as first class features, other languages are still able to implement their own interpretation of continuation-passing style so long as they have the right tools to do so. In order for a language to properly implement continuation-passing style, the language needs to be able to support closures and proper tail calls. This is due to the fact that for continuation-passing style to work properly, the language needs to be able to manually create continuations which can be implemented as non-returning functions that are able to encapsulate the respective name bindings within its lexical scope, and be able to support functions that are able to call other functions at the very end of themselves.
One way to implement continuation-passing style into a program is to have a function which takes in an additional argument of another function, which represents the continuation. When the first function completes its execution, instead of returning the result of the procedure, it instead passes this result into the continuation, thus advancing the program with its product. In contrast, the direct style of programming would rather return the result of the function back to the caller in order to further advance the program, while with the continuation-passing style, the next set of instructions for the program is being called with the result of the function when its ready.
The concept of asynchronous programming inherently lends itself to continuation-passing style; if a program is running independently of the main thread, how will the main program know when it has finished? The most common answer to this problem is to implement continuation-passing style for asynchronous functions. The idea is to pass in the continuation of the program to the asynchronous function, and once the function has completed its process, the result is then passed as an argument into the continuation, and the program can advance. All of this can occur without the program having to halt, since we aren’t waiting for the call to finish to continue running the program, but rather just giving the asynchronous call a set of instructions to perform with its result once it has completed.