You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: WEB-INTEGRATION.md
+120-21
Original file line number
Diff line number
Diff line change
@@ -426,27 +426,126 @@ Event dispatches can be one of the following:
426
426
some web API, but the dispatch happens at a later point. In these cases, the
427
427
context should be tracked along the data flow of the operation, even across
428
428
code running in parallel (but not through tasks enqueued on other agents'
429
-
event loops). [See below on implicit context
430
-
propagation](#implicit-context-propagation) for how this data flow tracking
431
-
should happen.
432
-
433
-
This classification of event dispatches is the way it should be in theory, as
434
-
well as a long-term goal. However, as we describe later in the section on
435
-
implicit context propagation, for the initial rollout we propose treating the
436
-
vast majority of asynchronous dispatches as if they were browser-originated.
437
-
The exceptions would be:
438
-
439
-
- The `popstate` event
440
-
- The `message` and `messageerror` events
441
-
- All events dispatched on `XMLHttpRequest` or `XMLHttpRequestUpload` objects
442
-
- The `unhandledrejection` and `rejectionhandled` events on the global object
443
-
(see below)
444
-
445
-
> TODO: The exact principle for which event listeners are included in this list is still under discussion.
446
-
447
-
The list above is not meant to be hard-coded in the events machinery as a "is this event part of that list?" check. Instead, the spec text and browser code that fires
448
-
each of these individual events would be modified so that it keeps track of the
449
-
context 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.
429
+
event loops).
430
+
431
+
For events triggered by JavaScript code (either synchronously or asynchronously),
432
+
the goal is for them to behave equivalently as if they were implemented by a
433
+
JavaScript developer that is not explicitly thinking about AsyncContext propagation:
434
+
listeners for events dispatched either **synchronously** or **asynchronously** from
435
+
JS or from a web API would use the context that API is called with.
436
+
437
+
<details>
438
+
<summary>Expand this section for examples of the equivalece with JS-authored code</summary>
439
+
440
+
Let's consider a simple approximation of the `EventTarget` interface, authored in JavaScript:
441
+
```javascript
442
+
classEventTarget {
443
+
#listeners = [];
444
+
445
+
addEventListener(type, listener) {
446
+
this.#listeners.push({ type, listener });
447
+
}
448
+
449
+
dispatchEvent(event) {
450
+
for (const { type, listener } ofthis.#listeners) {
451
+
if (type ===event.type) {
452
+
listener.call(this, event);
453
+
}
454
+
}
455
+
}
456
+
}
457
+
```
458
+
459
+
An example _synchronous_ event is `AbortSignal`'s `abort` event. A naive approximation
460
+
in JavaScript would look like the following:
461
+
462
+
```javascript
463
+
classAbortController {
464
+
constructor() {
465
+
this.signal=newAbortSignal();
466
+
}
467
+
468
+
abort() {
469
+
this.signal.aborted=true;
470
+
this.signal.dispatchEvent(newEvent("abort"));
471
+
}
472
+
}
473
+
```
474
+
475
+
When calling `abortController.abort()`, there is a current async context active in the agent. All operations that lead to the `abort` event being dispatched are synchronous and do not manually change the current async context: the active async context will remain the same through the whole `.abort()` process,
And lets trace how the context propagates from `.send()` in the following case:
521
+
```javascript
522
+
constasyncVar=newAsyncContext.Variable();
523
+
constxhr=newXMLHttpRequest();
524
+
xhr.open("GET", "https://example.com");
525
+
xhr.addEventListener("load", () => {
526
+
console.log(asyncVar.get()); // "foo"
527
+
});
528
+
asyncVar.run("foo", () => {
529
+
xhr.send();
530
+
});
531
+
```
532
+
- when `.send()` is called, the value of `asyncVar` is `"foo"`.
533
+
- it is synchronously propagated up to the `fetch()` call in `.send()`
534
+
- the `await` snapshots the context before pausing, and restores it (to `asyncVar: "foo"`) when the `fetch` completes
535
+
- the `await`s in the reader loop propagate the context as well
536
+
- when `this.dispatchEvent(new Event("load"))`, is called, the current active async context is thus
537
+
the same one as when `.send()` was called
538
+
- the `"load"` callback thus runs with `asyncVar` set to `"foo"`.
539
+
540
+
Note that this example uses `await`, but due to the proposed semantics for `.then` and `setTimeout`
541
+
(and similar APIs), the same would hapepn when using other asynchronicity primitives.
542
+
543
+
</details>
544
+
545
+
Event listeners for events dispatched **from the browser** rather than as a consequence of some JS action (e.g. a user clicking on a button) will by default run in the root (empty) context. This is the same
546
+
context that the browser uses, for example, for the top-level execution of scripts.
547
+
548
+
> NOTE: To keep agents isolated, events dispatched from different agents (e.g. from a worker, or from a cross-origin iframe) will behave as events dispatched by user interaction.
0 commit comments