Skip to content

Commit 86470fb

Browse files
authored
Fix unhandled promise rejection (#105)
1 parent 26e45e5 commit 86470fb

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

source/iterable.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@ export const lineIterator = async function * (subprocess, {state}, streamName) {
1616
return;
1717
}
1818

19+
handleErrors(subprocess);
1920
yield * readline.createInterface({input: stream});
2021
} finally {
2122
await subprocess;
2223
}
2324
};
2425

26+
// When the `subprocess` promise is rejected, we await it in the `finally`
27+
// block. However, this might not happen right away, so an `unhandledRejection`
28+
// error is emitted first, crashing the process. This prevents it.
29+
// This is safe since we are guaranteed to propagate the `subprocess` error
30+
// with the `finally` block.
31+
// See https://github.com/sindresorhus/nano-spawn/issues/104
32+
const handleErrors = async subprocess => {
33+
try {
34+
await subprocess;
35+
} catch {}
36+
};
37+
2538
// Merge two async iterators into one
2639
export const combineAsyncIterators = async function * (...iterators) {
2740
try {

test/iterable.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
fourthTestString,
99
multibyteString,
1010
} from './helpers/arguments.js';
11-
import {assertFail, assertErrorEvent} from './helpers/assert.js';
11+
import {assertAbortError, assertFail, assertErrorEvent} from './helpers/assert.js';
1212
import {
1313
nodeEval,
1414
nodePrintStdout,
@@ -19,6 +19,7 @@ import {
1919
nodePassThrough,
2020
nodePassThroughPrint,
2121
nodePassThroughPrintFail,
22+
nodeHanging,
2223
} from './helpers/commands.js';
2324

2425
const getIterable = (subprocess, iterableType) => iterableType === ''
@@ -222,6 +223,21 @@ const testStreamIterateAllError = async (t, streamName) => {
222223
test('Handles subprocess.stdout error in subprocess[Symbol.asyncIterator]', testStreamIterateAllError, 'stdout');
223224
test('Handles subprocess.stderr error in subprocess[Symbol.asyncIterator]', testStreamIterateAllError, 'stderr');
224225

226+
const testStreamIterateSignal = async (t, streamName) => {
227+
const signal = AbortSignal.timeout(0);
228+
const subprocess = spawn(...nodeHanging, {signal});
229+
const error = await t.throwsAsync(arrayFromAsync(subprocess[streamName]));
230+
assertAbortError(t, error, signal.reason);
231+
const promiseError = await t.throwsAsync(subprocess);
232+
t.is(promiseError, error);
233+
t.is(promiseError.stdout, '');
234+
t.is(promiseError.stderr, '');
235+
t.is(promiseError.output, '');
236+
};
237+
238+
test('Handles subprocess.stdout error with the "signal" option', testStreamIterateSignal, 'stdout');
239+
test('Handles subprocess.stderr error with the "signal" option', testStreamIterateSignal, 'stderr');
240+
225241
// eslint-disable-next-line max-params
226242
const iterateOnOutput = async (t, subprocess, state, cause, shouldThrow, iterableType) => {
227243
// eslint-disable-next-line no-unreachable-loop

0 commit comments

Comments
 (0)