【问题标题】:nodejs - Async generator/iterator with or without awaiting long operationnodejs - 有或没有等待长时间操作的异步生成器/迭代器
【发布时间】:2022-11-04 19:03:40
【问题描述】:

我试图了解哪种设置最适合执行以下操作:

  1. 逐行读取 CSV 文件
  2. 使用行数据作为复杂函数的输入,该函数最终输出一个文件(每行一个文件)
  3. 整个过程完成后,我需要压缩步骤 2 中生成的所有文件

    我的目标:能够处理大文件的快速且可扩展的解决方案

    我已经使用两种方法实现了第 2 步,我想知道什么是最好的以及为什么(或者是否有其他更好的方法)

    步骤1

    这很简单,我依赖CSV Parser - async iterator API

    async function* loadCsvFile(filepath, params = {}) {
      try {
        const parameters = {
          ...csvParametersDefault,
          ...params,
        };
        const inputStream = fs.createReadStream(filepath);
        const csvParser = parse(parameters);
        const parser = inputStream.pipe(csvParser)
        for await (const line of parser) {
          yield line;
        }
      } catch (err) {
        throw new Error("error while reading csv file: " + err.message);
      }
    }
    

    第2步

    选项1

    等待每一行的长操作handleCsvLine

    // step 1
    const csvIterator = loadCsvFile(filePath, options);
    // step 2
    let counter = 0;
    for await (const row of csvIterator) {
      await handleCvsLine(
        row,
      );
      counter++;
      if (counter % 50 === 0) {
        logger.debug(`Processed label ${counter}`);
      }
    }
    // step 3
    zipFolder(folderPath);
    

    • 很高兴看到文件一个接一个地生成
    • 因为它等待操作结束,所以我可以很好地显示进度

    缺点

    • 它等待每个操作,我可以更快吗?

    选项 2

    将长操作handleCsvLine 推入一个数组,然后在循环之后执行Promise.all

    // step 1
    const csvIterator = loadCsvFile(filePath, options);
    // step 2
    let counter = 0;
    const promises = [];
    for await (const row of csvIterator) {
      promises.push(handleCvsLine(row));
      counter++;
      if (counter % 50 === 0) {
        logger.debug(`Processed label ${counter}`);
      }
    }
    
    await Promise.all(promises);
    // step 3
    zipFolder(folderPath);
    

    • 我不等待,所以它应该更快,不是吗?

    缺点

    • 因为它不等待,所以 for 循环非常快,但最后有很长的等待时间(又名,糟糕的进度体验)

    第 3 步

    一个简单的步骤,我使用archiver 库创建文件夹的 zip,我在其中保存了步骤 2 中的文件:

    function zipFolder(folderPath, globPath, outputFolder, outputName, logger) {
      return new Promise((resolve, reject) => {
        // create a file to stream archive data to.
        const stream = fs.createWriteStream(path.join(outputFolder, outputName));
        const archive = archiver("zip", {
          zlib: { level: 9 }, // Sets the compression level.
        });
    
        archive.glob(globPath, { cwd: folderPath });
    
        // good practice to catch warnings (ie stat failures and other non-blocking errors)
        archive.on("warning", function (err) {
          if (err.code === "ENOENT") {
            logger.warning(err);
          } else {
            logger.error(err);
            reject(err);
          }
        });
    
        // good practice to catch this error explicitly
        archive.on("error", function (err) {
          logger.error(err);
          reject(err);
        });
    
        // pipe archive data to the file
        archive.pipe(stream);
        // listen for all archive data to be written
        // 'close' event is fired only when a file descriptor is involved
        stream.on("close", function () {
          resolve();
        });
        archive.finalize();
      });
    }
    

【问题讨论】:

    标签: node.js asynchronous async-await


    【解决方案1】:

    不使用await 不会使操作更快。它不会等待响应,而是移动到下一个操作。无论有没有await,它将继续向事件队列添加操作。

    您应该使用 child_process 来模拟并行处理。 Node js 不是多线程的,但您可以使用child_process 来实现它,它在 CPU 内核上运行。这样,您可以根据系统中可用的 CPU 内核数一次生成多个文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-28
      • 1970-01-01
      • 2019-07-21
      相关资源
      最近更新 更多