Chapter 17: Coordinating timelines
Introduces the need to coordinate timelines even when they do not share an explicit resource.
After this chapter you will be able to
- Coordination without shared resources. Explain why multiple timelines may need coordination even when they do not share an explicit resource.
- First-class manipulation of time. Explain the section’s proposal to treat time as a first-class concept rather than relying only on a language’s implicit model of time.
- Intermittent wrong cart totals. Describe the reported bug: the cart sometimes shows the wrong total even when only one item is added, and adding multiple items quickly is also affected.
- Effect of moved callback boundary. Explain how moving a closing brace and parenthesis changed when shipping_ajax() runs relative to cost_ajax(), and why that change is faster but buggy.
- Shared local variable in diagrams. Explain why the local variable total must be included when identifying actions for this timeline diagram, despite having been omitted from earlier diagrams.
- Drawing actions on timelines. Describe how step 2 diagrams the identified actions from scratch, including why the callback to update_total_dom() belongs in the shipping_ajax() callback’s timeline, before applying JavaScript simplifications.
- JavaScript timeline simplifications. Describe the two simplifications: put actions on one timeline into one box, then combine timelines when the first ends by creating only the second. Explain why the click-handler and queue timelines can be combined but the queue and AJAX callback timelines cannot.
- Callback ordering bug. Explain how the two AJAX callbacks can run in an undesirable order even when their requests are initiated in the right order, and identify total as the resource shared between timelines.
- Sequential and parallel wait times. Explain why parallel requests can update the DOM sooner than sequential requests, using the section’s three-second and four-second example, and state why the faster timeline is still faulty.
- Parallel callback coordination. Explain why both AJAX callbacks must finish before the DOM is updated, even though their responses can arrive in either order.
- Cut ordering boundary. Describe what a cut means on multiple timelines and how it simplifies their possible orderings.
- Completion counting across timelines. Explain how the proposed primitive lets multiple timelines finish in either order but continues only after all have finished.
- Cut placement and callback. Explain why Cut() is created in calc_cart_total() and what callback is passed to it.
- End-of-timeline done calls. Explain why done() must be called at the end of each timeline being cut.
- Orderings within timeline regions. Explain how the dotted lines divide the timeline for ordering analysis: the top and bottom regions each have only one timeline and one possible ordering, while the middle region has two timelines with one step each whose possible orderings still need checking.
- Parallel speed with coordinated ordering. Explain why waiting for both parallel AJAX responses takes only as long as the slower response while preserving the required ordering for one click.
- Queue and cut coordination. Explain why the shopping cart needs both a queue and Cut() when a user clicks add to cart quickly.
- Cut timeline analysis. Describe how Cut() reduces the ordering analysis for the multiple-click timeline.
- JustOnce callback behavior. Explain how JustOnce() wraps an action so that the first call has an effect and later calls do not, even when multiple timelines share it. Relate this behavior to the section’s use of the term idempotent.
- Implicit model of time. Explain the ordering and repetition rules in JavaScript’s implicit model of time, including how sequential statements, separate timelines, asynchronous events, and repeated calls behave.
- Explicit ordering and repetition. Describe how Queue(), Cut(), JustOnce(), and DroppingQueue() change the default ordering or repetition of work.
- Timeline reduction priorities. Explain why reducing the number of timelines comes first, followed by shortening each timeline. Describe the changes the section suggests for doing both.
- Shared resource strategies. Distinguish eliminating shared resources from sharing them safely with concurrency primitives, and state which approach the summary prefers.
- Reliable results through coordination. Explain why the web-request race condition sometimes produced the wrong result and how coordinating the two timelines addressed it.
What it builds on
- Building a queue in JavaScript (chapter 16) · Introduces a queue for safely sharing a resource between timelines.
- Timelines that share resources can cause problems (chapter 15) · Recognizing that timelines sharing resources can cause problems
- We need to guarantee the order of the DOM updates (chapter 16) · The need to guarantee an order for updates
- Timeline diagrams capture the uncertain ordering of parallel code (chapter 15) · Introduces uncertain ordering when code runs in parallel.
- Simplifying the timeline (chapter 15) · Earlier simplification of timeline diagrams when details can safely be omitted.
- JavaScript’s single-thread (chapter 15) · Introduces JavaScript’s single thread.
- The two fundamentals of timeline diagrams (chapter 15) · the fundamentals of timeline diagrams
- First-class functions can replace any syntax (chapter 10) · using first-class functions to represent behavior
Section by section
Introduction
Introduces the need to coordinate timelines even when they do not share an explicit resource.
- Coordination without shared resources: Explain why multiple timelines may need coordination even when they do not share an explicit resource.
Why it matters: It broadens the timeline problem beyond the resource-sharing bugs addressed in the previous chapter.
Key terms: timelines, coordinate, possible orderings
17.1 Principles of working with timelines
This section summarizes ways to make timelines easier to reason about and introduces treating time as something code can manipulate directly.
- Timeline ordering complexity: Explain why fewer timelines, shorter timelines, and less sharing between timelines make possible action orderings easier to analyze.
Why it matters: Reducing what can interact makes concurrency problems easier to inspect. - Coordination of shared resources: Explain what coordinating timelines accomplishes when they share resources.
Why it matters: Coordination is useful because some otherwise possible orderings produce incorrect results. - First-class manipulation of time: Explain the section’s proposal to treat time as a first-class concept rather than relying only on a language’s implicit model of time.
Why it matters: Treating call order and repetition as things you can control helps fit execution to the problem.
Key terms: timelines, possible orderings, shared resources, coordinate, first-class concept, reusable objects, implicit model of time, ordering of calls, repetition of calls
17.2 There’s a bug!
The section introduces an intermittent wrong-total bug in the shopping cart after UI speed optimizations.
- Intermittent wrong cart totals: Describe the reported bug: the cart sometimes shows the wrong total even when only one item is added, and adding multiple items quickly is also affected.
Why it matters: Recognizing that a single addition can fail keeps the investigation focused on the simplest reported case.
Key terms: shopping cart queue, wrong total
17.3 How the code was changed
The section explains how a small code change made two AJAX requests run at the same time, improving speed but introducing a bug.
- Effect of moved callback boundary: Explain how moving a closing brace and parenthesis changed when shipping_ajax() runs relative to cost_ajax(), and why that change is faster but buggy.
Why it matters: A small change in callback placement can change the timing of requests and introduce a bug.
Key terms: shipping_ajax(), cost_ajax() callback, ajax requests
17.4 Identify actions: Step 1
The section explains why a local variable must be included when identifying actions for a new timeline diagram if multiple timelines access it.
- Shared local variable in diagrams: Explain why the local variable total must be included when identifying actions for this timeline diagram, despite having been omitted from earlier diagrams.
Why it matters: Including shared access at the start keeps the diagram from hiding a possible timing problem.
Key terms: timeline diagram, total, multiple timelines, simplify
17.5 Draw each action: Step 2
This section completes the action-by-action timeline diagram, placing the final callback on the timeline where it runs before simplifying the diagram.
- Drawing actions on timelines: Describe how step 2 diagrams the identified actions from scratch, including why the callback to update_total_dom() belongs in the shipping_ajax() callback’s timeline, before applying JavaScript simplifications.
Why it matters: Drawing the actions before simplifying keeps assumptions from an earlier diagram out of the new one.
Key terms: timeline diagram, actions, shipping_ajax(), update_total_dom(), simplify
17.6 Simplify the diagram: Step 3
The section simplifies a JavaScript timeline diagram and identifies a variable shared across the remaining timelines.
- JavaScript timeline simplifications: Describe the two simplifications: put actions on one timeline into one box, then combine timelines when the first ends by creating only the second. Explain why the click-handler and queue timelines can be combined but the queue and AJAX callback timelines cannot.
Why it matters: Simplifying the diagram makes the remaining interactions between timelines easier to see. - Shared total variable: Explain what the simplified diagram reveals about access to the total variable.
Why it matters: The simplified diagram points to the resource whose access needs closer examination.
Key terms: consolidate actions, consolidate timelines, JavaScript threading model, shared resource, total variable, local variable
17.7 Possible ordering analysis
The section identifies a callback ordering that causes a bug despite the requests being initiated in the intended order.
- Callback ordering bug: Explain how the two AJAX callbacks can run in an undesirable order even when their requests are initiated in the right order, and identify total as the resource shared between timelines.
Why it matters: Checking callback order reveals a bug that the order of the requests alone would not show.
Key terms: total, shipping_ajax() callback, cost_ajax() callback, timelines
17.8 Why this timeline is faster
The section uses timeline diagrams to explain why parallel AJAX requests can reduce wait time while their responses can still arrive in an unpredictable order.
- Sequential and parallel wait times: Explain why parallel requests can update the DOM sooner than sequential requests, using the section’s three-second and four-second example, and state why the faster timeline is still faulty.
Why it matters: This explains both the appeal of parallel requests and why speed alone does not make the change correct. - Unpredictable AJAX response order: Explain why sending cost_ajax before shipping_ajax does not guarantee that its response arrives first, using a cause described in the section.
Why it matters: Request order cannot be used as a guarantee of response order.
Key terms: timeline, parallel, sequence, DOM, cost_ajax, shipping_ajax, response payload
17.9 Waiting for both parallel callbacks
The section explains how a cut lets two parallel AJAX callbacks finish before their shared total is written to the DOM.
- Parallel callback coordination: Explain why both AJAX callbacks must finish before the DOM is updated, even though their responses can arrive in either order.
Why it matters: Waiting for both results preserves parallel responses without displaying an incomplete total. - Cut ordering boundary: Describe what a cut means on multiple timelines and how it simplifies their possible orderings.
Why it matters: The boundary makes parallel timelines easier to reason about by ruling out many possible orderings.
Key terms: ajax responses, callbacks, total local variable, DOM, cut, timelines, possible orderings
17.10 A concurrency primitive for cutting timelines
This section explains how a completion-counting primitive can coordinate timelines that finish in different orders, and why JavaScript can implement it with a simple variable.
- Completion counting across timelines: Explain how the proposed primitive lets multiple timelines finish in either order but continues only after all have finished.
Why it matters: Counting completions lets the code wait for all timelines without depending on which finishes first. - Finish order and race conditions: Describe the race condition this coordination is meant to prevent.
Why it matters: It explains why merely allowing parallel work to finish is not enough when the next step depends on both results. - Single-threaded counter safety: Explain why the primitive can use a simple mutable counter in JavaScript, and how the section contrasts that with multithreaded languages.
Why it matters: The simple counter relies on a JavaScript-specific timing guarantee, not on mutable state being safe in every language.
Key terms: primitive, timelines, callback, race condition, single thread, mutable variable, atomic update
17.11 Using Cut() in our code
The section shows where to put Cut() in the add-to-cart code, what callback to give it, and why done() must be called at the end of each timeline.
- Cut placement and callback: Explain why Cut() is created in calc_cart_total() and what callback is passed to it.
Why it matters: Putting Cut() where both callbacks are created lets them coordinate before the follow-up callback runs. - End-of-timeline done calls: Explain why done() must be called at the end of each timeline being cut.
Why it matters: An early done() call can make the coordinated callback run while work on a timeline still continues. - Simple reusable coordination primitive: Describe what Cut() does and how the section advises choosing between it and an existing primitive.
Why it matters: The coordination idea is simple enough to reuse without depending on a particular language’s API.
Key terms: Cut(), done(), calc_cart_total(), update_total_dom(), timeline, concurrency primitive, Promise.all()
17.12 Uncertain ordering analysis
The section divides the add-to-cart timeline into regions to check which action orderings need analysis before claiming the parallel version behaves correctly.
- Orderings within timeline regions: Explain how the dotted lines divide the timeline for ordering analysis: the top and bottom regions each have only one timeline and one possible ordering, while the middle region has two timelines with one step each whose possible orderings still need checking.
Why it matters: Checking each region keeps the correctness question focused on the part where ordering can vary.
Key terms: timeline, dotted lines, possible ordering
17.13 Parallel execution analysis
The section checks that coordinating two parallel AJAX responses preserves the speed benefit while keeping the required ordering for one click.
- Parallel speed with coordinated ordering: Explain why waiting for both parallel AJAX responses takes only as long as the slower response while preserving the required ordering for one click.
Why it matters: Coordinating the responses fixes the one-click timing bug without giving up parallel execution speed.
Key terms: parallel execution, cut the timeline, cost_ajax(), shipping_ajax()
17.14 Multiple-click analysis
Analyzes how a queue and Cut() handle multiple shopping-cart clicks, then considers which sources of concurrency complexity are worth changing.
- Queue and cut coordination: Explain why the shopping cart needs both a queue and Cut() when a user clicks add to cart quickly.
Why it matters: The two tools address different timing problems that occur in the same button interaction. - Cut timeline analysis: Describe how Cut() reduces the ordering analysis for the multiple-click timeline.
Why it matters: Separating parallel work from sequential work makes the behavior easier to check. - Sources of concurrency complexity: Distinguish the three sources of complexity in this example and explain the tradeoffs in removing them.
Why it matters: Some complexity can be relocated or avoided only by changing product and architecture choices you may want to keep.
Key terms: queue, Cut(), ajax requests, timeline diagram, possible orderings, asynchronous web requests, API responses, uncertainty of the user’s interactions
17.15 A primitive to call something just once
The section introduces JustOnce(), a reusable concurrency primitive that lets multiple timelines share an action while allowing it to have an effect only once.
- JustOnce callback behavior: Explain how JustOnce() wraps an action so that the first call has an effect and later calls do not, even when multiple timelines share it. Relate this behavior to the section’s use of the term idempotent.
Why it matters: It gives multiple timelines a reusable way to share an action without repeating its effect. - Shared state coordination safety: Explain why JustOnce() can safely use a shared mutable variable in the JavaScript described here, and what kind of coordination a multithreaded language would need instead.
Why it matters: The once-only behavior depends on how timelines coordinate access to their shared state.
Key terms: JustOnce(), concurrency primitive, higher-order function, idempotent, mutable variable, timeline, atomic update
17.16 Implicit versus explicit model of time
The section contrasts JavaScript’s implicit rules for ordering and repetition with concurrency primitives that make those rules explicit.
- Implicit model of time: Explain the ordering and repetition rules in JavaScript’s implicit model of time, including how sequential statements, separate timelines, asynchronous events, and repeated calls behave.
Why it matters: These default rules make it possible to see what a concurrency primitive changes. - Explicit ordering and repetition: Describe how Queue(), Cut(), JustOnce(), and DroppingQueue() change the default ordering or repetition of work.
Why it matters: Choosing a primitive lets you state the execution rules the program needs instead of relying on language defaults. - First-class actions in primitives: Explain why making actions first-class helps build concurrency primitives, without assuming every primitive must be a higher-order function.
Why it matters: Treating an action as a value lets code move or wrap its execution without rewriting the action.
Key terms: implicit model of time, ordering, repetition, timelines, explicit model of time, Queue(), Cut(), JustOnce(), DroppingQueue(), first-class actions
17.17 Summary: Manipulating timelines
The section summarizes five ways to improve multiple timelines, ordered from most to least preferred.
- Timeline reduction priorities: Explain why reducing the number of timelines comes first, followed by shortening each timeline. Describe the changes the section suggests for doing both.
Why it matters: Simplifying timelines can remove ordering problems before you need tools to manage them. - Shared resource strategies: Distinguish eliminating shared resources from sharing them safely with concurrency primitives, and state which approach the summary prefers.
Why it matters: Removing sharing avoids ordering issues; safe sharing is the fallback when sharing cannot be removed. - Timeline coordination purpose: Describe what coordinating timelines with primitives such as promises and cuts is meant to constrain.
Why it matters: Coordination lets you control when actions happen or repeat when simpler timeline changes are not enough.
Key terms: timelines, actions, calculations, implicit inputs and outputs, shared resources, queues, locks, concurrency primitives, promises, cuts
17.18 Conclusion
The chapter concludes that coordinating timelines makes the result reliable even when web requests return in an unpredictable order.
- Reliable results through coordination: Explain why the web-request race condition sometimes produced the wrong result and how coordinating the two timelines addressed it.
Why it matters: Coordinating timelines prevents an unpredictable response order from changing the outcome.
Key terms: race condition, coordination between timelines, primitive
17.19 Summary
17.20 Up next…
This section closes the discussion of first-class functions and timelines and previews a discussion of design focused on onion architecture.
Where this is used later
- Tradeoffs of the reactive architecture (chapter 18) · Analyzes uncertain orderings of actions.
- Big takeaways (chapter 19) · Manipulating timelines
- Production: Eliminate a bug today (chapter 19) · The previously taught skills for manipulating timelines
Concepts explained in this chapter
- concurrency primitiveA synchronization construct that provides a defined operation for coordinating concurrent execution paths or managing shared resources.
- cutting timelineA coordination technique that draws a boundary across timelines so actions before it precede actions after it.
- implicit time modelThe representation of time and execution behavior provided implicitly by a programming language or runtime.
- complexityA measure of the difficulty of understanding or analyzing a piece of software based on its structure or behavior.
- parallelismExecution in which multiple actions may occur simultaneously or without a fixed order relative to one another.
- sequential processingA sequential action is an action that is ordered to occur in sequence with other actions on the same execution timeline.
- single-threaded execution modelA single-threaded runtime is an execution environment in which program actions execute on one thread at a time.
- actionAn action is a computation whose observable result depends on when or how it is performed and may involve effects beyond producing a value.
- mathematical calculationA calculation is a category of code that deterministically computes a result from its inputs without directly interacting with the outside world.
- scopeThe region of a program in which a binding can be accessed or referenced.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
