【问题标题】:ForEach async js still not solvedForEach 异步 js 仍未解决
【发布时间】:2021-06-04 15:28:24
【问题描述】:

我仍然无法掌握如何解决这个问题。我尝试了How do I add a delay in a JavaScript loop? 中给出的解决方案,据说这些解决方案可以解决我的问题,但它们对我没有用。

要么我得到我尝试过的相同的东西(一个运行一次但不能解决我的问题的等待功能)。或者在运行脚本时,脚本在 cli 和我希望将结果写入的 Result.txt 文件中都没有输出。

var path = require('path');
var fs = require('fs');
var VirusTotalApi = require("virustotal-api");
var virusTotal = new VirusTotalApi('<YOUR API KEY>');

fs.readdir('/home/username/Desktop/TEST/', function (err,files) {
  if (err) {
    return console.log('Unable to scan directory: ' +err);
  }

  files.forEach(function (file) {
    var directoryPath = path.join('/home/username/Desktop/TEST/', file);

    fs.readFile(directoryPath, (err, data) => {
      if (err) {
        console.log(`Cannot read file. ${err}`);
      } else {
        console.log(file);
        virusTotal
        .fileScan(data, `${file}`)
        .then(response => {
          let resource = response.resource;
      virusTotal.fileReport(resource).then(result => {
            fs.writeFile('Result.txt', `${file}: ` + JSON.stringify(result, ["verbose_msg","total","positives"]) + '\n', function (err) {
              if (err) throw err;
              if(!err) {
                console.log(`${file}: Saved!`);
              }
            }); 
          });
        })
        .catch(err => console.log(`Scan failed. ${err}`));
        if(!err) {
          console.log('Scan succesful');
        }
      }
    });
  });
});

再一次,我需要一个等待 20 秒的 ForEach 循环,然后运行所需的代码。在进入下一个 for 条件之前,它还需要等待。

【问题讨论】:

  • 等待时间正好 20 秒 的原因是什么?您是否正在等待所需的操作发生并假设需要 20 秒?
  • 鹿深,是的。正如我在第一个问题 (stackoverflow.com/questions/67838518/foreach-async-js) 中提到的那样,该问题已过早关闭,免费 API 密钥的限制是每分钟 4 个文件扫描。所以,我需要做一个等待 20 秒的 ForEach 循环,然后运行所需的代码。实际上,我发现上传部分可以同时运行,无需在文件之间等待。我可以推送 xy 文件进行扫描,我有一个脚本可以做到这一点。然后它等待 100 秒,但这只是因为如果我立即请求结果,我只会得到“您的资源已排队等待分析”。
  • 请求结果部分是限制每分钟可以运行多少次的部分。如果我运行它太快,我会得到一个错误。因此,需要 20 秒,即更新为 30 秒,现在就像一个魅力。

标签: javascript node.js for-loop


【解决方案1】:

插入暂停并不能解决异步问题,也不能暂停 .forEach() 循环。相反,您需要编写能够让您控制异步操作​​的顺序和完成的异步代码类型。

对所有异步操作使用 Promise 并避免诸如 .forEach() 之类的非异步感知循环结构将使这更容易。这是处理它的一种方法:

const path = require('path');
const fsp = require('fs').promises;
const VirusTotalApi = require("virustotal-api");
const virusTotal = new VirusTotalApi('<YOUR API KEY>');

const basePath = '/home/username/Desktop/TEST/';

async function scan() {
    const files = await fsp.readdir(basePath);
    let errors = [];
    for (let file of files) {
        const fullPath = path.join(basePath, file);
        try {
            const data = await fsp.readFile(fullPath);
            const response = await virusTotal.fileScan(data, file);
            const resource = response.resource;
            const result = await virusTotal.fileReport(resource);
            const resultLine = `${file}: ${JSON.stringify(result)}\n`;
            await fsp.appendFile('Result.txt', resultLine);
        } catch (e) {
            // collect the error, log the error and continue the loop
            e.fullPath = fullPath;
            errors.push(e);
            console.log(`Error processing ${fullPath}`, e);
            continue;
        }
    }
    // if there was an error, then reject with all the errors we got
    if (errors.length) {
        let e = new Error("Problems scanning files");
        e.allErrors = errors;
        throw e;
    }
}

scan().then(() => {
    console.log("all done scanning - no errors");
}).catch(err => {
    console.log(err);
});

另外,在您使用 fs.writeFile('Result.txt', ...) 的地方,我将其更改为使用 .appendFile(),因为您似乎想要累积结果日志,而不是每次都覆盖所有以前的结果。

【讨论】:

  • 亲爱的朋友,您的代码组织得非常好。我在 const basePath const wait=(time)=>new Promise((resolve)=>setTimeout(resolve, time)); 之后添加了下一行我在 for loop const sata=await fsp.readFile('Result.txt'); 之后添加了下一行if(sata.includes(file)){ console.log(${file} &gt;&gt; already scanned!);} else{ 然后 const fullPath continue 我在 for loop end await wait(30000);代码完美运行。我有一个新问题。如果文件没有被扫描,我希望它等待 30 秒。如果文件被扫描,我不希望它等待 30 秒。可以做到吗?谢谢。
  • 很抱歉昨天没有回复。我用了一天的所有 500 次扫描,所以我无法测试您的解决方案。
【解决方案2】:

基于https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

正如我所见,使用 forEach 函数在每次回调调用之间等待 20 秒是不可能的。 forEach 不适用于 Promises,因此无法在其中等待。

有一个解决方案,它使用 for 循环和异步容器。

var path = require('path');
var fs = require('fs');
var VirusTotalApi = require("virustotal-api");
var virusTotal = new VirusTotalApi('<YOUR API KEY>');

const wait = (time) => new Promise((resolve) => setTimeout(resolve, time))

fs.readdir('/home/username/Desktop/TEST/', async function (err,files) {
  if (err) {
    return console.log('Unable to scan directory: ' +err);
  }

  for (let file of files) {
    var directoryPath = path.join('/home/username/Desktop/TEST/', file);

    fs.readFile(directoryPath, (err, data) => {
      if (err) {
        console.log(`Cannot read file. ${err}`);
      } else {
        console.log(file);
        virusTotal
        .fileScan(data, `${file}`)
        .then(response => {
          let resource = response.resource;
      virusTotal.fileReport(resource).then(result => {
            fs.writeFile('Result.txt', `${file}: ` + JSON.stringify(result, ["verbose_msg","total","positives"]) + '\n', function (err) {
              if (err) throw err;
              if(!err) {
                console.log(`${file}: Saved!`);
              }
            }); 
          });
        })
        .catch(err => console.log(`Scan failed. ${err}`));
        if(!err) {
          console.log('Scan succesful');
        }
      }
    });

    // Wait for 20 seconds
    await wait(20 * 1000)
  }
});

【讨论】:

  • 亲爱的 Nikita,我在 for 循环之后添加了这些行: fs.readFile('Result.txt', async function (err, sata){ if (err) throw err; if(sata.includes( file)){ console.log(${file}&gt;&gt;already scanned!);} else{...然后 var directoryPath 继续我还将时间从 20 秒更改为 30 秒。代码运行良好。我有一个新问题。我添加的部分检查文件Result.txt作为文件名,所以它不需要再次扫描相同的文件。这部分效果很好。问题是如果文件已经扫描,我不希望它等待30秒而是立即继续。可以完成吗? Спасибо
  • 很抱歉昨天没有回复。我用了一天的所有 500 次扫描,所以我无法测试您的解决方案。
猜你喜欢
  • 1970-01-01
  • 2017-06-16
  • 2017-11-05
  • 2022-11-12
  • 2016-11-08
  • 1970-01-01
  • 2018-03-03
  • 2019-09-29
  • 2019-06-14
相关资源
最近更新 更多