@@ -534,6 +534,70 @@ asyncVar.run("A", async () => {
534534This increases the integrity of async context variables, and makes them
535535easier to reason about where a value of an async variable comes from.
536536
537+ ## How does ` AsyncContext ` interact with built-in schedulers?
538+
539+ Any time a scheduler (such as ` setTimeout ` , ` addEventListener ` , or
540+ ` Promise.prototype.then ` ) runs a user-provided callback, it must choose which
541+ snapshot to run it in. While userland schedulers are free to make any choice
542+ here, this proposal adopts a convention that built-in schedulers will always run
543+ callbacks in the snapshot that was active when the callback was passed to the
544+ built-in (i.e. at "registration time"). This is equivalent to what would happen
545+ if the user explicitly called ` AsyncContext.Snapshot.wrap ` on all callbacks
546+ before passing them.
547+
548+ This choice is the most consistent with the function-scoped structure that
549+ results from ` run ` taking a function, and is also the most clearly-defined
550+ option among the possible alternatives. For instance, many event listeners
551+ may be initiated either programmatically or through user interaction; in the
552+ former case there may be a more recently relevant snapshot available, but it's
553+ inconsistent across different types of events or even different instances of the
554+ same type of event. On the other hand, passing a callback to a built-in function
555+ happens at a very clearly defined time.
556+
557+ Another advantage of registration-time snapshotting is that it is expected to
558+ reduce the amount of intervention required to opt out of the default snapshot.
559+ Because ` AsyncContext ` is a subtle feature, it's not reasonable to expect every
560+ web developer to build a complete understanding of its nuances. Moreover, it's
561+ important that library users should not need to be aware of the nature of the
562+ variables that library implementations are implicitly passing around. It would
563+ be harmful if common practices emerged that developers felt they needed to wrap
564+ their callbacks before passing them anywhere. The primary means to have a
565+ function run in a different snapshot is to call ` Snapshot.wrap ` , but this
566+ will be idempotent when passing callbacks to built-ins, making it both less
567+ likely for this common practice to begin in the first place, and also less
568+ harmful when it does happen unnecessarily.
569+
570+ ## What if I need access to the snapshot from a more recent cause?
571+
572+ The downside to registration-time snapshotting is that it's impossible to opt
573+ _ out_ of the snapshot restoration to access whatever the snapshot would have
574+ been _ before_ it was restored. Use cases where this snapshot is more relevant
575+ include
576+
577+ - programmatically-dispatched events whose handlers are installed at application
578+ initialization time
579+ - unhandled rejection handlers are a specific example of the above
580+ - tracing execution flow, where one task "follows from" a sibling task
581+
582+ As explained above, the alternative snapshot choices are much more specific to
583+ the individual use case, but they can be made available through side channels.
584+ For instance, web specifications could include that certain event types will
585+ expose an ` originSnapshot ` property (actual name to be determined) on the event
586+ object containing the active ` AsyncContext.Snapshot ` from a specific point in
587+ time that initiated the event.
588+
589+ Providing these additional snapshots through side channels has several benefits
590+ over switching to them by default, or via a generalized "previous snapshot"
591+ mechanism:
592+
593+ - different types of schedulers may have a variety of potential origination
594+ points, whose scope can be matched precisely with a well-specified side
595+ channel
596+ - access via a known side channel avoids loss of idempotency when callbacks are
597+ wrapped multiple times (whereas a "previous snapshot" would becomes much less
598+ clear)
599+ - no single wrapper method for developers to build bad habits around
600+
537601# Prior Arts
538602
539603## zones.js
0 commit comments