-
Notifications
You must be signed in to change notification settings - Fork 29.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
stream: close iterator in Readable.from
Use for-of loop to traverse iterator and properly close it if not all of its values are consumed. Fixes: #32842
- Loading branch information
Showing
2 changed files
with
177 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
'use strict'; | ||
|
||
const { mustCall, mustNotCall } = require('../common'); | ||
const { Readable } = require('stream'); | ||
const { strictEqual } = require('assert'); | ||
|
||
async function asyncSupport() { | ||
const finallyMustCall = mustCall(); | ||
async function* generate() { | ||
try { | ||
yield 'a'; | ||
mustNotCall('only first item is read'); | ||
} finally { | ||
finallyMustCall(); | ||
} | ||
} | ||
|
||
const stream = Readable.from(generate()); | ||
|
||
for await (const chunk of stream) { | ||
strictEqual(chunk, 'a'); | ||
break; | ||
} | ||
} | ||
|
||
asyncSupport().then(mustCall()); | ||
|
||
async function syncSupport() { | ||
const finallyMustCall = mustCall(); | ||
function* generate() { | ||
try { | ||
yield 'a'; | ||
mustNotCall('only first item is read'); | ||
} finally { | ||
finallyMustCall(); | ||
} | ||
} | ||
|
||
const stream = Readable.from(generate()); | ||
|
||
for await (const chunk of stream) { | ||
strictEqual(chunk, 'a'); | ||
break; | ||
} | ||
} | ||
|
||
syncSupport().then(mustCall()); | ||
|
||
async function syncPromiseSupport() { | ||
const finallyMustCall = mustCall(); | ||
function* generate() { | ||
try { | ||
yield Promise.resolve('a'); | ||
mustNotCall('only first item is read'); | ||
} finally { | ||
finallyMustCall(); | ||
} | ||
} | ||
|
||
const stream = Readable.from(generate()); | ||
|
||
for await (const chunk of stream) { | ||
strictEqual(chunk, 'a'); | ||
break; | ||
} | ||
} | ||
|
||
syncPromiseSupport().then(mustCall()); | ||
|
||
async function syncRejectedSupport() { | ||
const finallyMustCall = mustCall(); | ||
const noBodyCall = mustNotCall(); | ||
const catchMustCall = mustCall(); | ||
|
||
function* generate() { | ||
try { | ||
yield Promise.reject('a'); | ||
mustNotCall(); | ||
} finally { | ||
finallyMustCall(); | ||
} | ||
} | ||
|
||
const stream = Readable.from(generate()); | ||
|
||
try { | ||
for await (const chunk of stream) { | ||
noBodyCall(chunk); | ||
} | ||
} catch { | ||
catchMustCall(); | ||
} | ||
} | ||
|
||
syncRejectedSupport().then(mustCall()); | ||
|
||
async function noReturnAfterThrow() { | ||
const returnMustNotCall = mustNotCall(); | ||
const noBodyCall = mustNotCall(); | ||
const catchMustCall = mustCall(); | ||
|
||
const stream = Readable.from({ | ||
[Symbol.asyncIterator]() { return this; }, | ||
async next() { throw new Error('a'); }, | ||
async return() { returnMustNotCall(); return { done: true }; }, | ||
}); | ||
|
||
try { | ||
for await (const chunk of stream) { | ||
noBodyCall(chunk); | ||
} | ||
} catch { | ||
catchMustCall(); | ||
} | ||
} | ||
|
||
noReturnAfterThrow().then(mustCall()); |