If a single event handler triggers three separate state updates, naively re-rendering once per update would do 3x the work for a result the user only ever sees once (the DOM only updates before the next paint anyway). Both React and Vue solve this the same way: don't run an update immediately -- QUEUE it, deduplicated by "job identity" (the same component's pending re-render, requested three times, is still only ONE pending job), then run the whole queue together on the next tick.
A "job" is any hashable callable -- calling it performs the actual update. Implement:
Scheduler.schedule(job)-- addjobto the pending queue, UNLESS it's already queued (the same job object scheduled twice before the next flush must not be queued twice).Scheduler.flush()-- run every currently queued job exactly once, in the order it was first scheduled this round, then clear the queue so the nextschedule()calls start fresh. A job that schedules ANOTHER job as a side effect of running (schedule()called from inside a job's own callback) must NOT have that new job run in this sameflush()call -- it waits for the next explicitflush().
flush() stands in for the real microtask tick (real Vue schedules a
flush via Promise.resolve().then(...); this exercise uses an explicit
method call the test invokes directly instead of a real microtask).
Example
scheduler = Scheduler()
render_count = 0
def rerender():
global render_count
render_count += 1
scheduler.schedule(rerender)
scheduler.schedule(rerender) # same job -- deduplicated
scheduler.schedule(rerender) # same job -- deduplicated
scheduler.flush()
render_count # -> 1, not 3
Constraints
- Dedup is by job IDENTITY (the same callable object), not by anything about what the job does.
flush()on an empty queue must not raise.- A job scheduled while
flush()is running must be picked up by the NEXTflush()call, never the current one.
Real-world grounding
- Vue core's job queue and flush:
packages/runtime-core/src/scheduler.ts-- seequeueJob's dedup check andqueueFlush/flushJobs, which this exercise implements a simplified version of.
Real Vue's queue is additionally sorted by component tree depth before flushing (so a parent's update always runs before its children's, never the reverse) and guards against infinite self-rescheduling loops with a recursion limit -- this exercise implements the core dedup-then-batch- flush mechanism underneath both.