liveBook · Manning

Scheduler

As explained in Build a Frontend Web Framework (From Scratch)

A scheduler is a mechanism that queues deferred callbacks and executes them according to an ordering policy.

A scheduler queues deferred callbacks and runs them after the current synchronous work finishes. In a component framework, it prevents asynchronous onMounted() work from blocking the application's first render. section 13.0

The browser event loop executes all pending microtasks, then the oldest eligible task, and finally allows the DOM to render. Microtasks therefore provide ordered, post-stack execution for lifecycle jobs. section 13.3.3 section 13.3.4

A small scheduler separates the mechanism into four responsibilities:

  • Store jobs in an array with enqueueJob(job). section 13.3.4
  • Schedule processJobs() with queueMicrotask() when the first job arrives. section 13.3.4
  • Use isScheduled to avoid scheduling duplicate processing callbacks. section 13.3.4
  • Dequeue and execute jobs in insertion order, including jobs added during processing, then reset the scheduling flag. section 13.3.5

Lifecycle integration

After creating a component DOM node, enqueue its onMounted() hook. After removing one, enqueue its onUnmounted() hook. The hooks run after mount or unmount completes and retain their scheduling order. section 13.3.6

Listing 13.3 The scheduler (scheduler.js)
let isScheduled = false                    #1
const jobs = []                        #2

export function enqueueJob(job) {
  jobs.push(job)                     #3
  scheduleUpdate()                 #4
}

function scheduleUpdate() {
  if (isScheduled) return

  isScheduled = true
  queueMicrotask(processJobs)      #5
}

function processJobs() {
  while (jobs.length > 0) {        #6
    const job = jobs.shift()
    job()
  }

  isScheduled = false       #7
}

Use the scheduler listing to inspect the complete queue, single-scheduling guard, microtask handoff, processing loop, and state reset. section 13.3.5

console.log('Start')
setTimeout(() => console.log('Timeout'))
queueMicrotask(() => console.log('Microtask 1'))
enqueueJob(() => console.log('Job'))
queueMicrotask(() => console.log('Microtask 2'))
console.log('End')

Trace console output alongside a timeout and explicit microtasks to reason about task and microtask ordering. section 13.3.5

Jobs may return promises. Wrapping each result with Promise.resolve() prevents floating promises and logs rejected jobs without interrupting the scheduler's processing loop. section 13.3.5

How Build a Frontend Web Framework (From Scratch) covers it

How deep it goes
Implements queued jobs, microtask scheduling, duplicate prevention, ordered processing, and promise rejection logging.
Examples
Uses mounted and unmounted hooks, named components, console output, timeouts, and explicit microtasks as ordering exercises.
What it assumes
Assumes JavaScript execution, browser event-loop behavior, and component lifecycle hooks while explaining the scheduling mechanism incrementally.
What you can do after
Build a JavaScript scheduler with queueMicrotask() and apply it to component lifecycle execution.
How it connects
Links lifecycle hooks with the event loop, microtask queue, task queue, and DOM rendering.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Build a Frontend Web Framework (From Scratch). The section links point to the parts of the book it draws on.