- From: Ardis Lu <notifications@github.com>
- Date: Mon, 28 Apr 2025 00:22:43 -0700
- To: whatwg/streams <streams@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/streams/issues/1344@github.com>
ardislu created an issue (whatwg/streams#1344)
### What is the issue with the Streams Standard?
If `cancel` is called immediately after a new `ReadableStream` is instantiated, the logic in `start` may not be completed before `cancel` executes. For example:
```javascript
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const r = new ReadableStream({
async start() {
await sleep(1000);
console.log('start completed');
},
cancel() {
console.log('cancel completed');
}
});
await r.cancel();
// cancel completed
// start completed
```
As I understand it, this behavior is by design. However, it may be confusing because `close` and `abort` on `WritableStream` *do* wait for `start` to complete:
```javascript
const w = new WritableStream({
async start() {
await sleep(1000);
console.log('start completed');
},
close() {
console.log('close completed');
}
});
await w.close();
// start completed
// close completed
const w2 = new WritableStream({
async start() {
await sleep(1000);
console.log('start completed');
},
abort() {
console.log('abort completed');
}
});
await w2.abort();
// start completed
// abort completed
```
**In particular**, examples [10.4](https://streams.spec.whatwg.org/#example-rs-pull) and [10.5](https://streams.spec.whatwg.org/#example-rbs-pull) on the standard imply that `cancel` waits for `start` to finish before executing because there is no check that `fs.open` has completed before trying to call `fileHandle.close`:
```javascript
const fs = require("fs").promises;
function makeReadableFileStream(filename) {
let fileHandle;
return new ReadableStream({
async start() {
fileHandle = await fs.open(filename, "r");
},
// ...
cancel() {
return fileHandle.close(); // Dangerous: fileHandle may still be undefined
}
});
}
```
It would be useful if examples 10.4 and 10.5 made an explicit note about this behavior. The example code can also be updated to block `cancel` until `start` is completed. For example:
```diff
const fs = require("fs").promises;
function makeReadableFileStream(filename) {
let fileHandle;
+ const { promise, resolve } = Promise.withResolvers();
return new ReadableStream({
async start() {
fileHandle = await fs.open(filename, "r");
+ resolve();
},
// ...
- cancel() {
+ async cancel() {
+ await promise;
return fileHandle.close();
}
});
}
```
--
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/issues/1344
You are receiving this because you are subscribed to this thread.
Message ID: <whatwg/streams/issues/1344@github.com>
Received on Monday, 28 April 2025 07:22:47 UTC