Skip to content

Commit 63159a3

Browse files
authored
Add explainer for WritableStreamDefaultController's AbortSignal
1 parent 1246ccb commit 63159a3

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# `WritableStream` controller `AbortSignal` Explainer
2+
3+
4+
## Introduction
5+
6+
The streams APIs provide ubiquitous, interoperable primitives for creating, composing, and consuming streams of data.
7+
8+
This change permits an underlying sink to rapidly abort an ongoing write or close when requested by the writer.
9+
10+
Previously, when `writer.abort()` was called, a long-running write would still have to continue to completion before
11+
the stream could be aborted. With this change, the write can be aborted immediately.
12+
13+
An underlying sink which doesn't observe the `controller.signal` will continue to have the existing behavior.
14+
15+
In addition to being exposed to streams authored in JavaScript, this facility will also be used by platform-provided
16+
streams such as [WebTransport](https://w3c.github.io/webtransport/).
17+
18+
19+
## API Proposed
20+
21+
On [WritableStreamDefaultController](https://streams.spec.whatwg.org/#writablestreamdefaultcontroller)
22+
(the controller argument that is passed to underlying sinks):
23+
24+
* [`abortReason`](https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortreason): The argument passed
25+
to `writable.abort()` or `writer.abort()`. Undefined if no argument was passed or `abort()` hasn't been called.
26+
* [`signal`](https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-signal): An AbortSignal. By using
27+
`signal.addEventListener('abort', …)` an underlying sink can abort the pending write or close operation when the
28+
stream is aborted.
29+
30+
The `WritableStream` API does not change. Instead, the existing `abort()` operation will now signal abort.
31+
32+
33+
## Examples
34+
35+
These are some examples of JavaScript which can be used for writable streams once this is implemented:
36+
37+
In this example, the underlying sink write waits 1 second to simulate a long-running operation. However, if abort() is
38+
called it stops immediately.
39+
40+
41+
```javascript
42+
const ws = new WritableStream({
43+
write(controller) {
44+
return new Promise((resolve, reject) => {
45+
setTimeout(resolve, 1000);
46+
controller.signal.addEventListener('abort',
47+
() => reject(controller.abortReason));
48+
});
49+
}
50+
});
51+
const writer = ws.getWriter();
52+
53+
writer.write(99);
54+
await writer.abort();
55+
```
56+
57+
58+
This example shows integration with an existing API that uses `AbortSignal`. In this case, each `write()` triggers a
59+
POST to a remote endpoint using [the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). The signal
60+
is used to abort the ongoing fetch when the stream is aborted.
61+
62+
63+
```javascript
64+
const endpoint = 'https://endpoint/api';
65+
const ws = new WritableStream({
66+
async write(controller, chunk) {
67+
const response = await fetch(endpoint, { signal: controller.signal,
68+
method: 'POST',
69+
body: chunk });
70+
await response.text();
71+
}
72+
});
73+
const writer = ws.getWriter();
74+
75+
writer.write('some data');
76+
await writer.abort();
77+
```
78+
79+
This example shows a use case of this feature with WebTransport.
80+
81+
```javascript
82+
const wt = new WebTransport(...);
83+
await wt.ready;
84+
const ws = await wt.createUnidirectionalStream();
85+
// `ws` is a WritableStream.
86+
87+
const reallyBigArrayBuffer = …;
88+
writer.write(reallyBigArrayBuffer);
89+
// Send RESET_STREAM to the server without waiting for `reallyBigArrayBuffer` to
90+
// be transmitted.
91+
await writer.abort();
92+
```
93+
94+
95+
96+
## Goals
97+
98+
* Allow writes to be aborted more quickly and efficiently.
99+
* WebTransport will be able to use WritableStreamDefaultController.signal to make
100+
[`SendStream`'s `write()`](https://w3c.github.io/webtransport/#sendstream-write) and
101+
[`close()`](https://w3c.github.io/webtransport/#sendstream-close) abortable.
102+
103+
104+
## Non-Goals
105+
106+
* Exposing a method to abort individual operations without aborting the stream as a whole. The semantics of this
107+
would be unclear and confusing.
108+
109+
110+
## User Benefits
111+
112+
* Allows the abort operation to complete more quickly, which avoids wasted resources for sites that take advantage of it.
113+
114+
115+
## Alternatives
116+
117+
* It was initially proposed that an `AbortSignal` could be passed to each sink `write()` call. However, since the
118+
abort signal does not need to change between two `write()` calls, it was thought better to just add a `signal` property
119+
on `WritableStreamDefaultController`.

0 commit comments

Comments
 (0)