在没有回调的情况下使用 Node.js 流

问题描述

要将 PDF 文件从 Node.js 服务器发送到客户端,我使用以下代码

const pdf = printer.createPdfKitDocument(docDeFinition);

const chunks = [];

pdf.on("data",(chunk) => {
    chunks.push(chunk);
});

pdf.on("end",() => {
    const pdfBuffered = `data:application/pdf;base64,${Buffer.concat(chunks).toString("base64")}`;
    res.setHeader("Content-Type","application/pdf");
    res.setHeader("Content-Length",pdfBuffered.length);
    res.send(pdfBuffered);
});

pdf.end();

一切正常,唯一的问题是这里的流使用的是回调方法,而不是 async/await

我发现了一个 possible solution

const { pipeline } = require("stream/promises");

async function run() {
    await pipeline(
        fs.createReadStream('archive.tar'),zlib.createGzip(),fs.createWriteStream('archive.tar.gz')
    );

    console.log('Pipeline succeeded.');
}

run().catch(console.error);

但我不知道如何将初始代码应用于带有 stream/promises代码

解决方法

如果回调只执行一次,则只能将回调 API 转换为 async/await

您在网上找到的那个有效,因为您只是在回调运行一次之前等待整个流完成。您得到的是对每个传入数据块执行多次的回调。

您可以查看其他资源来使流更易于使用,例如 RXJSthis 即将推出的 ECMAScript 提案,以将可观察对象添加到语言中。这两者都旨在处理回调可以多次执行的情况——这是 async/await 无法做到的。

,

您可以像这样手动将 PDF 代码包装在一个 promise 中,然后将其用作返回 promise 的函数:

function sendPDF(docDefinition) {
    return new Promise((resolve,reject) => {
        const pdf = printer.createPdfKitDocument(docDefinition);

        const chunks = [];

        pdf.on("data",(chunk) => {
            chunks.push(chunk);
        });

        pdf.on("end",() => {
            const pdfBuffered =
                `data:application/pdf;base64,${Buffer.concat(chunks).toString("base64")}`;
            resolve(pdfBuffered);
        });

        pdf.on("error",reject);

        pdf.end();
    });
}

sendPDF(docDefinition).then(pdfBuffer => {
    res.setHeader("Content-Type","application/pdf");
    res.setHeader("Content-Length",pdfBuffer.length);
    res.send(pdfBuffer);
}).catch(err => {
    console.log(err);
    res.sendStatus(500);
});

因为有很多 data 事件,所以不能只承诺数据部分。您仍然需要监听每个 data 事件并收集数据。