@@ -448,14 +448,95 @@ The list above is not meant to be hard-coded in the events machinery as a "is th
448448each of these individual events would be modified so that it keeps track of the
449449context in which these events were scheduled (e.g. the context of ` window.postMessage ` or ` xhr.send() ` ), and so that that context is restored before firing the event.
450450
451- ### Fallback context
451+ ### Fallback context ( [ # 107 ] ( https://github.com/tc39/proposal-async-context/issues/107 ) )
452452
453453This use of the empty context for browser-originated dispatches, however,
454454clashes with the goal of allowing “isolated” regions of code that share an event
455455loop, and being able to trace in which region an error originates. A solution to
456- this would be the ability to define a fallback context for a region of code. We
457- have a proposal for this being fleshed out at issue
458- [ #107 ] ( https://github.com/tc39/proposal-async-context/issues/107 ) .
456+ this would be the ability to define fallback values for some ` AsyncContext.Variable ` s
457+ when the browser runs some JavaScript code due to a browser-originated dispatch.
458+
459+ ``` javascript
460+ const widgetID = new AsyncContext.Variable ();
461+
462+ widgetID .run (" weather-widget" , () => {
463+ captureFallbackContext (widgetID, () => {
464+ renderWeatherWidget ();
465+ });
466+ });
467+ ```
468+
469+ In this example, event listeners registered by ` renderWeatherWidget ` would be guaranteed
470+ to always run as a consequence of some "widget": if the event is user-dispatched, then
471+ it defaults to ` weather-widget ` rather than to ` widgetID ` 's default value (` undefined ` ,
472+ in this case).
473+
474+ <details >
475+ <summary >Full example</summary >
476+
477+ ``` javascript
478+ const widgetID = new AsyncContext.Variable ();
479+
480+ widgetID .run (" weather-widget" , () => {
481+ captureFallbackContext (widgetID, () => {
482+ renderWeatherWidget ();
483+ });
484+ });
485+
486+ addEventListener (" unhandledrejection" , event => {
487+ console .error (` Unhandled rejection in widget "${ widgetID .get ()} "` );
488+ // Handle the rejection. For example, disable the widget, or report
489+ // the error to a server that can then notify the widget's developers.
490+ });
491+ ```
492+
493+ ``` javascript
494+ function renderWeatherWidget () {
495+ let day = Temporal .Now .plainDate ();
496+
497+ const widget = document .createElement (" div" );
498+ widget .innerHTML = `
499+ <button id =" prev" >Previous day</button >
500+ <output >...</output >
501+ <button id =" next" >Next day</button >
502+ ` ;
503+ document .body .appendChild (widget);
504+
505+ const load = async () => {
506+ const response = await fetch (` /weather/${ day} ` );
507+ widget .querySelector (" output" ).textContent = await response .text ();
508+ };
509+
510+ widget .querySelector (" #prev" ).addEventListener (" click" , async () => {
511+ day = day .subtract ({ days: 1 });
512+ await load ();
513+ });
514+ widget .querySelector (" #next" ).addEventListener (" click" , async () => {
515+ day = day .add ({ days: 1 });
516+ await load ();
517+ });
518+
519+ load ();
520+ }
521+ ```
522+
523+ When the user clicks on one of the buttons and the ` fetch ` it triggers fails,
524+ without using ` captureFallbackContext ` the ` unhandledrejection ` event listener
525+ would not know that the failure is coming from the ` weather-widget ` widget.
526+
527+ Thanks to ` captureFallbackContext ` , that information is properly propagated.
528+
529+ </details >
530+
531+ This fallback is per-variable and not based on ` AsyncContext.Snapshot ` , to avoid
532+ accidentally keeping alive unnecessary objects.
533+
534+ There are still some questions about ` captureFallbackContext ` that need to be
535+ answered:
536+ - should it take just one variable or a list of variables?
537+ - should it just be for event targets, or for all web APIs that can be triggered
538+ by non-JS code?
539+ - should it be a global, or a static method of ` EventTarget ` ?
459540
460541## Script errors and unhandled rejections
461542
0 commit comments