Demystifying JavaScript Continuations and the Latest Front‑End Trends
This weekly tech roundup explains the continuation concept in JavaScript, shows how CallCC underpins React hooks, highlights 2020 front‑end trends like low‑code, serverless and AI‑driven UI generation, and reports on recent Flutter web developments and a controversial COVID‑related news story.
Continuation in JavaScript
Continuation is the abstract representation of the remaining computation at any point in a program. The default continuation is the call stack, which follows a strict last‑in‑first‑out (LIFO) order. This LIFO constraint prevents flexible reordering, early exit, or back‑tracking.
The language design keeps a call‑stack continuation because it provides a simple foundation for exception handling, generators, coroutines, and algebraic effects.
Heap‑allocated call frames
To break the LIFO limitation, each logical task can be represented as a call‑frame object allocated on the heap. Frames are linked via a pointer, forming a list (or a graph). When a task finishes, the runtime follows the pointer to the next frame, effectively jumping to a non‑linear continuation.
Continuation‑Passing Style (CPS) implementation
In CPS every function receives an extra argument – the continuation – which is a function that receives the result and continues the computation. This makes the flow of control explicit and composable.
function add(a, b, k) {
// k is the continuation that receives the sum
k(a + b);
}
add(2, 3, function sum => console.log(sum)); // prints 5Unlike ordinary callbacks, CPS does not rely on implicit stack unwinding; the continuation is passed forward, enabling tail‑call optimisation and easier composition.
callCC (call‑with‑current‑continuation)
callCC captures the current continuation as a first‑class value. The captured continuation can be stored, invoked later, or discarded, allowing non‑linear control flow such as early exit, back‑tracking, or coroutine switching.
function callCC(f) {
return new Promise(resolve => {
const cont = x => resolve(x); // captured continuation
f(cont);
});
}
// Example: early return using the captured continuation
callCC(k => {
if (condition) return k('early');
// normal path continues here
return compute();
}).then(v => console.log(v));JavaScript libraries like js-csp or async‑generator patterns emulate callCC semantics by storing the continuation in a channel or iterator.
Generators as a concrete callCC use‑case
Generator functions pause at yield, implicitly capture the current continuation, and resume when .next() is called. This maps directly to callCC where the continuation is the remainder of the generator body.
function* counter() {
let i = 0;
while (true) {
i = yield i; // pause, capture continuation, resume with new i
}
}
const it = counter();
console.log(it.next().value); // 0
console.log(it.next(5).value); // 5React APIs built on continuation concepts
useStateand other hooks store stateful continuations in a fiber linked list; each render is a continuation that can be resumed when state changes. Suspense captures the point where a component suspends, stores the continuation, and later re‑enters when the awaited promise resolves.
Concurrent Mode schedules multiple continuations (fibers) on a priority queue, allowing the runtime to pause, abort, or reorder work without losing logical progress.
All three mechanisms rely on the ability to capture, store, and later invoke a continuation—essentially the capability provided by callCC.
Deep understanding requires tracing the React source (e.g., the react-reconciler repository on GitHub) to see how fibers are linked, how scheduleWork manipulates the continuation list, and how the scheduler decides which continuation to run next.
Qborfy AI
A knowledge base that logs daily experiences and learning journeys, sharing them with you to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
