mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
pipeline() accepts a source function (an async generator function called
with { signal }) and a destination async function, following
pipeline.js: the destination's promise value resolves the
stream/promises form and reaches the callback's second argument, and
pipeline() answers a PassThrough for it. A rejected destination destroys
that PassThrough with the reason and fails the pipeline.
Stages may take the { signal } argument: the pipeline creates its own
AbortController when one does, and aborts it when the pipeline finishes
or fails, as finishImpl does. Stage functions take the source and
{ signal } through the closure ABI, and the PassThrough and the value
are released with the pipeline.
The stream unit now links the emitter unit whenever it links, so a
pipeline of functions builds without constructing a stream. An async
generator destination and reading the PassThrough a function destination
returns are refused at compile time.
105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
// pipeline() function ends: a destination that rejects, a source that
|
|
// throws (its signal aborts with an AbortError first), the callback's order
|
|
// against microtasks and a tick, a stream source with a { signal } stage,
|
|
// and a destination that resolves to undefined.
|
|
import { pipeline } from "node:stream";
|
|
import { pipeline as pipelineP } from "node:stream/promises";
|
|
|
|
const log: string[] = [];
|
|
let n = 0;
|
|
const chain = (): void => {
|
|
if (n < 30) {
|
|
n++;
|
|
log.push(`m${n}`);
|
|
queueMicrotask(chain);
|
|
}
|
|
};
|
|
function flush(label: string): void {
|
|
console.log(label, log.join(" "));
|
|
log.length = 0;
|
|
n = 0;
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
// a destination that rejects
|
|
try {
|
|
await pipelineP(
|
|
async function* () {
|
|
yield "a";
|
|
},
|
|
async function (source: AsyncIterable<string>) {
|
|
for await (const c of source) log.push(`got ${c}`);
|
|
throw new Error("dest failed");
|
|
},
|
|
);
|
|
} catch (err) {
|
|
log.push(`caught ${(err as Error).message}`);
|
|
}
|
|
flush("dest rejects");
|
|
// a source that throws, with the stage's signal observed
|
|
try {
|
|
await pipelineP(
|
|
async function* ({ signal }: { signal: AbortSignal }) {
|
|
signal.addEventListener("abort", () => log.push(`source abort ${(signal.reason as Error).name}`));
|
|
yield "x";
|
|
throw new Error("source failed");
|
|
},
|
|
async function (source: AsyncIterable<string>) {
|
|
for await (const c of source) log.push(`dest ${c}`);
|
|
return "unreached";
|
|
},
|
|
);
|
|
} catch (err) {
|
|
log.push(`caught ${(err as Error).message}`);
|
|
}
|
|
flush("source throws");
|
|
// the callback form's order against microtasks and ticks
|
|
await new Promise<void>((resolve) => {
|
|
queueMicrotask(chain);
|
|
process.nextTick(() => log.push("tick"));
|
|
pipeline(
|
|
async function* () {
|
|
yield "p";
|
|
yield "q";
|
|
},
|
|
async function (source: AsyncIterable<string>) {
|
|
let s = "";
|
|
for await (const c of source) {
|
|
log.push(`chunk ${c}`);
|
|
s += c;
|
|
}
|
|
return s;
|
|
},
|
|
(err?: Error | null, value?: string) => {
|
|
log.push(`callback ${err ? err.message : null} ${value}`);
|
|
resolve();
|
|
},
|
|
);
|
|
});
|
|
flush("timing");
|
|
// a stream source, a stage with { signal }, and a promise destination
|
|
const r = await pipelineP(
|
|
(await import("node:stream")).Readable.from(["1", "2", "3"]),
|
|
async function* (source: AsyncIterable<string>, { signal }: { signal: AbortSignal }) {
|
|
for await (const c of source) yield `${c}:${signal.aborted}`;
|
|
},
|
|
async function (source: AsyncIterable<string>) {
|
|
const parts: string[] = [];
|
|
for await (const c of source) parts.push(c);
|
|
return parts.join(",");
|
|
},
|
|
);
|
|
console.log("stream source", r);
|
|
// a destination that returns nothing
|
|
const none = await pipelineP(
|
|
async function* () {
|
|
yield "z";
|
|
},
|
|
async function (source: AsyncIterable<string>) {
|
|
for await (const c of source) log.push(c);
|
|
},
|
|
);
|
|
console.log("void destination", none, log.join(","));
|
|
}
|
|
void main();
|