Skip to content

Commit aa75dd8

Browse files
Remove fallback context (#139)
1 parent 130f1d3 commit aa75dd8

File tree

1 file changed

+2
-105
lines changed

1 file changed

+2
-105
lines changed

WEB-INTEGRATION.md

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ Event dispatches can be one of the following:
203203
user actions, or by cross-agent JS, with no involvement from JS code in the
204204
same agent. Such dispatches can't have propagated any context from some non-existing
205205
JS code that triggered them, so the listener is called with the empty context.
206-
(Though see the section on fallback context below.)
207206
- **Asynchronous dispatches**, where the event originates from JS calling into
208207
some web API, but the dispatch happens at a later point. In these cases, the
209208
context should be tracked along the data flow of the operation, even across
@@ -333,106 +332,6 @@ context that the browser uses, for example, for the top-level execution of scrip
333332
> [!WARNING]
334333
> To keep agents isolated, events dispatched from different agents (e.g. from a worker, or from a cross-origin iframe) will behave like events dispatched by user interaction. This also applies to events dispatched from cross-origin iframes in the same agent, to avoid exposing the fact that they're in the same agent.
335334
336-
### Fallback context ([#107](https://github.com/tc39/proposal-async-context/issues/107))
337-
338-
This use of the empty context for browser-originated dispatches, however,
339-
clashes with the goal of allowing "isolated" regions of code that share an event
340-
loop, and being able to trace in which region an error originates. A solution to
341-
this would be the ability to define fallback values for some `AsyncContext.Variable`s
342-
when the browser runs some JavaScript code due to a browser-originated dispatch.
343-
344-
```javascript
345-
const widgetID = new AsyncContext.Variable();
346-
347-
widgetID.run("weather-widget", () => {
348-
captureFallbackContext(widgetID, () => {
349-
renderWeatherWidget();
350-
});
351-
});
352-
```
353-
354-
In this example, event listeners registered by `renderWeatherWidget` would be guaranteed
355-
to always run as a consequence of some "widget": if the event is user-dispatched, then
356-
it defaults to `weather-widget` rather than to `widgetID`'s default value (`undefined`,
357-
in this case). There isn't a single global valid default value, because a page might have
358-
multiple widgets that thus need different fallbacks.
359-
360-
<details>
361-
<summary>Expand this section to read the full example</summary>
362-
363-
This complete example shows that when clicking on a button (thus, without a JavaScript cause
364-
that could propagate the context), some asynchronous operations start. These operations
365-
might reject, firing a `unhandledrejection` event on the global object.
366-
367-
If there was no fallback context, the `"click"` event would run with `widgetID` unset, that
368-
would thus be propagated unset to `unhandledrejection` as well. Thanks to `captureFallbackContext`,
369-
the user-dispatched `"click"` event will fallback to running with `widgetID` set to
370-
`"weather-widget"`, which will then be propagated to `unhandledrejection`.
371-
372-
```javascript
373-
const widgetID = new AsyncContext.Variable();
374-
375-
widgetID.run("weather-widget", () => {
376-
captureFallbackContext(widgetID, () => {
377-
renderWeatherWidget();
378-
});
379-
});
380-
381-
addEventListener("unhandledrejection", event => {
382-
console.error(`Unhandled rejection in widget "${widgetID.get()}"`);
383-
// Handle the rejection. For example, disable the widget, or report
384-
// the error to a server that can then notify the widget's developers.
385-
});
386-
```
387-
388-
```javascript
389-
function renderWeatherWidget() {
390-
let day = Temporal.Now.plainDate();
391-
392-
const widget = document.createElement("div");
393-
widget.innerHTML = `
394-
<button id="prev">Previous day</button>
395-
<output>...</output>
396-
<button id="next">Next day</button>
397-
`;
398-
document.body.appendChild(widget);
399-
400-
const load = async () => {
401-
const response = await fetch(`/weather/${day}`);
402-
widget.querySelector("output").textContent = await response.text();
403-
};
404-
405-
widget.querySelector("#prev").addEventListener("click", async () => {
406-
day = day.subtract({ days: 1 });
407-
await load();
408-
});
409-
widget.querySelector("#next").addEventListener("click", async () => {
410-
day = day.add({ days: 1 });
411-
await load();
412-
});
413-
414-
load();
415-
}
416-
```
417-
418-
When the user clicks on one of the buttons and the `fetch` it triggers fails,
419-
without using `captureFallbackContext` the `unhandledrejection` event listener
420-
would not know that the failure is coming from the `weather-widget` widget.
421-
422-
Thanks to `captureFallbackContext`, that information is properly propagated.
423-
424-
</details>
425-
426-
This fallback is per-variable and not based on `AsyncContext.Snapshot`, to avoid
427-
accidentally keeping alive unnecessary objects.
428-
429-
There are still some questions about `captureFallbackContext` that need to be
430-
answered:
431-
- should it take just one variable or a list of variables?
432-
- should it just be for event targets, or for all web APIs that take a callback
433-
which can run when triggered from outside of JavaScript? (e.g. observers)
434-
- should it be a global, or a static method of `EventTarget`?
435-
436335
## Status change listener callbacks
437336

438337
These APIs register a callback or constructor to be invoked when some action
@@ -496,8 +395,7 @@ context to propagate:
496395
form would queue a microtask to call its `formResetCallback` lifecycle hook,
497396
and there would not be a causal context.
498397

499-
Similarly to events, in this case lifecycle callbacks would run in the empty context, with
500-
the [fallback context mechanism](#fallback-context-107).
398+
Similarly to events, in this case lifecycle callbacks would run in the empty context.
501399

502400
## Observers
503401

@@ -510,8 +408,7 @@ Observer callbacks are not called once per observation. Instead, multiple observ
510408
can be batched into one single call. This means that there is not always a single JS action
511409
that causes some work that eventually triggers the observer callback; rather, there might be many.
512410

513-
Given this, observer callbacks should always run with the empty context, using the same
514-
[fallback context mechanism](#fallback-context-107) as for events. This can be explained
411+
Given this, observer callbacks should always run with the empty context. This can be explained
515412
by saying that, e.g. layout changes are always considered to be a browser-internal trigger, even if
516413
they were caused by changes injected into the DOM or styles through JavaScript.
517414

0 commit comments

Comments
 (0)