Asynchronous and Continuation-Based Programming
Asynchronous programming lets a program initiate work and continue without blocking the current execution path while waiting.
14.1 Asynchronous programming
Asynchronous programming lets a program initiate work and continue without blocking the current execution path while waiting. It is useful for network and disk I/O, timers, UI interactions, and many service workloads.
Asynchrony does not automatically mean parallel execution.
14.2 Promises and async/await
await suspends the async function until the promise settles; it does not necessarily block the entire runtime thread. The type assertion is not runtime validation, so the response schema must be checked in production.
type Course = { id: string; title: string };
async function fetchCourse(id: string): Promise<Course> {
const response = await fetch(`/api/courses/${encodeURIComponent(id)}`);
if (!response.ok) {
throw new Error("Unable to load course");
}
const data: unknown = await response.json();
// Production code should validate `data` against a runtime schema.
return data as Course;
}14.3 Error propagation and cancellation
Handle rejected promises.
Use try/catch around operations whose failures can be handled locally.
Propagate errors when the caller is responsible for handling them.
Support cancellation and timeouts for long-running operations.
Avoid unhandled background tasks.
Clean up timers, subscriptions, and resources.
14.4 Sequential versus concurrent awaits
This starts both operations and waits for both results. Promise.all rejects if one input rejects, but it does not automatically cancel every other operation. Use cancellation support where necessary. Bound concurrency when processing large collections.
Try it: sequential versus concurrent awaits The tracer below (shared with the Concurrency & Async Programming course) shows when each operation starts and how errors propagate, for sequential awaits, Promise.all, allSettled and a task group. Latencies are simulated; the ordering rules follow the language semantics shown in its source view.
const [profile, courses] = await Promise.all([
fetchProfile(),
fetchCourses()
]);14.5 Continuations
A continuation represents “what to do next.” Callbacks, promise chains, and async/await can all express continuation-based control flow in different forms. Compilers may transform async functions into state machines.
14.6 Event loop and task queues
JavaScript runtimes coordinate synchronous execution, queued tasks, microtasks, and I/O callbacks according to host-specific event-loop rules. Long synchronous computation can block UI or server progress. Move expensive work to suitable workers or services when needed.
14.7 Concurrency limits
Launching thousands of requests simultaneously may exhaust sockets, memory, service quotas, or rate limits. Use a bounded pool, queue, or concurrency limiter. Define retry, timeout, cancellation, and partial-failure policy.
Exercise
Fetch 100 course records using unbounded Promise.all, then implement a bounded-concurrency version. Compare peak load, elapsed time, failure behavior, and cancellation.
Quiz
? Does `await` block the whole JavaScript thread?
+ No — it suspends the async function; other tasks can run meanwhile. :: That is what makes async code non-blocking.
- Yes, until the promise settles. :: Only the async function is suspended.
- Only in browsers. :: The same applies in Node.js.
- Only for network requests. :: It applies to any awaited promise.
? `Promise.all([a(), b()])` where `a()` rejects first. What happens to `b()`?
+ It keeps running; its result is ignored unless you cancel it. :: Promise.all does not cancel other operations.
- It is cancelled automatically. :: Promises have no built-in cancellation.
- It never started. :: Both started before Promise.all was called.
- Promise.all waits for it before rejecting. :: It rejects as soon as one input rejects.
? Why limit concurrency when fetching thousands of records? (select all that apply)
+ To avoid exhausting sockets and memory :: Each in-flight request costs resources.
+ To respect service quotas and rate limits :: The remote side may throttle or block.
+ To make failures and cancellation manageable :: A bounded pool is easier to control.
- Because JavaScript can only run two requests at once :: There is no suc
…