JavaScript NodeJS 如何在异步函数中使用流/承诺?

3

我在Node中有一个JS异步函数。假设它从URL下载文件并对其进行一些操作,例如解压缩。我将其编写成这样,它可以工作,但是eslint向我显示了一个臭味相当浓的代码: error Promise executor functions should not be async no-async-promise-executor。因为函数体中有await fetch,所以需要使用Async

我不熟练使用streamsasync/await来自己纠正它。我想摆脱Promise,并完全使用async/await。从 Node-15 开始,模块stream/promises似乎是去的方式,如此评论how-to-use-es8-async-await-with-streams。在这种情况下如何使用await pipeline(...)?也许有更好、更短的方法?

这是函数:

function doSomething(url) {
  return new Promise(async (resolve, reject) => {
    try {
      const fileWriteStream = fs.createWriteStream(someFile, {
        autoClose: true,
        flags: 'w',
      });

      const res = await fetch(url);
      const body = res.body;
      body
        .pipe(fileWriteStream)
        .on('error', (err) => {
          reject(err);
        })
        .on('finish', async () => {
          await doWhatever();
          resolve('DONE');
        });
    } catch (err) {
      reject(err);
    }
  });
}

不确定这是否与您有关,但async function doSomething对您没有任何作用...因为您返回一个Promise,所以它应该只是function doSomething - LostJon
好的,我确实是从一些较大的代码中提取了这个函数,但它确实与此处的主题无关。我将其从我的示例中删除。 - jgran
3个回答

3

在到达执行器之前,您可以简单地执行await

async function doSomething(url) {
  
  const fileWriteStream = fs.createWriteStream(someFile, { autoClose: true, flags: 'w' });
  
  let { body } = await fetch(url);
  
  body.pipe(fileWriteStream);
  
  return new Promise((resolve, reject) => {
    body.on('error', reject);
    body.on('finish', resolve);
  });
  
};

我的一般建议是尽可能从承诺执行程序中删除尽可能多的代码。 在这种情况下,Promise仅需要捕获解析/拒绝。

请注意,我还从doSomething中删除了doWhatever - 这使得doSomething更加健壮。 您只需执行以下操作:

doSomething('http://example.com').then(doWhatever);

最后,我建议你将 someFile 设置为 doSomething 的参数,而不是从某个更广泛的上下文中引用它!

谢谢。你的代码确实非常干净,与我那个糟糕的代码完全相反。抱歉someFile放错了位置,在编写好的代码片段中出现了错误。 - jgran
非常整洁和干净的解决方案,但一开始我一直在想为什么它不起作用。后来我发现了关键问题:回调签名中的 rejectresolve 参数已经颠倒了。换句话说,返回语句应该以 return new Promise((resolve, reject) => { 开始。 - Michael Sorens
@MichaelSorens 哎呀,好发现!已修复! - Gershom Maes

3
要使用你正在寻找的pipeline函数,应该这样操作:
const { pipeline } = require('stream/promises');

async function doSomething(url) {
  const fileWriteStream = fs.createWriteStream(someFile, {
    autoClose: true,
    flags: 'w',
  });

  const res = await fetch(url);
  await pipeline(res.body, fileWriteStream);
  await doWhatever();
  return 'DONE';
}

1
你可以在NodeJS中使用fs/promises,并将代码简化为以下内容:
import { writeFile } from 'fs/promises'

async function doSomething(url) {
    const res = await fetch(url);
    if (!res.ok) throw new Error('Response not ok');
    await writeFile(someFile, res.body, { encoding: 'utf-8'})
    await doWhatever();
    return 'DONE';
  });
}

你使用 fetch(url).then 来避免使用 async (resolve, reject) =>...,但我的问题是是否只在函数体中使用 async/awaitawait fetch(url)?或者作为替代方案,如果可能的话,使用 stream/promisespipeline 函数? - jgran
抱歉,我已经更新了我的回答。流对于你尝试完成的简单事情来说有点过于复杂了。 - Ryan Wheale
谢谢!我不知道writeFile。代码更加简洁了!使用pipelinecreateWriteStream也是可行的,类似于pipeline( ~将fetch作为流~,fs.createWriteStream) - jgran
我正在使用命名导入 - import { writeFile } from 'fs/promises';。你也可以这样做:import fs from 'fs/promises';,这样你就可以得到大部分以 promises 形式提供的文件系统 API。然后,你可以等待大多数文件系统操作:await fs.writeFile(...);。此外,如果这解决了你的问题,最好接受答案,这样其他人就知道它起作用了。干杯! - Ryan Wheale
当然会!谢谢!我还在消化到目前为止的答案。 - jgran
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接