nano-spawn
Version:
Tiny process execution for humans — a better child_process
55 lines (46 loc) • 1.45 kB
JavaScript
import * as readline from 'node:readline/promises';
export const lineIterator = async function * (subprocess, {state}, streamName) {
// Prevent buffering when iterating.
// This would defeat one of the main goals of iterating: low memory consumption.
if (state.isIterating === false) {
throw new Error(`The subprocess must be iterated right away, for example:
for await (const line of spawn(...)) { ... }`);
}
state.isIterating = true;
try {
const {[streamName]: stream} = await subprocess.nodeChildProcess;
if (!stream) {
return;
}
yield * readline.createInterface({input: stream});
} finally {
await subprocess;
}
};
// Merge two async iterators into one
export const combineAsyncIterators = async function * (...iterators) {
try {
let promises = [];
while (iterators.length > 0) {
promises = iterators.map((iterator, index) => promises[index] ?? getNext(iterator));
// eslint-disable-next-line no-await-in-loop
const [{value, done}, index] = await Promise.race(promises
.map((promise, index) => Promise.all([promise, index])));
const [iterator] = iterators.splice(index, 1);
promises.splice(index, 1);
if (!done) {
iterators.push(iterator);
yield value;
}
}
} finally {
await Promise.all(iterators.map(iterator => iterator.return()));
}
};
const getNext = async iterator => {
try {
return await iterator.next();
} catch (error) {
await iterator.throw(error);
}
};