@@ -534,6 +534,70 @@ asyncVar.run("A", async () => {
534
534
This increases the integrity of async context variables, and makes them
535
535
easier to reason about where a value of an async variable comes from.
536
536
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
+
537
601
# Prior Arts
538
602
539
603
## zones.js
0 commit comments