array.forEach()如何处理异步函数?

时间:2019-01-11 20:05:22

标签: javascript node.js

我正在尝试遍历关键字列表,并为每个关键字调用mysql.query()。

问题是,我需要每个查询调用在下一个查询开始之前结束。我该如何实现?

我曾尝试使其与Promise请求一起使用,但我不完全了解异步调用的工作原理。

keywords.forEach(keyword => {
    sql.query("Select Id from Keyword Where Word= ?", [keyword], 
                function (err, ids) {...}
});

6 个答案:

答案 0 :(得分:1)

您可以递归进行

function call(keywords, index){
    if(keyworkds[index]){
       sql.query("Select Id from Keyword Where Word= ?", [keyworkds[index].keyword], 
          function (err, ids) {
               call(keywords, index + 1)
         }
    }
}

call(keywords, 0)

答案 1 :(得分:0)

forEach等待异步功能完成。您需要传递回调的原因是,这是完成后必须执行某些操作的唯一真实方法。

因此,forEach根本无法工作。可以重写此代码以使第二次迭代异步发生,但是这很丑陋。

我建议(如果可以的话)切换到async / await并使用支持Promise的mysql客户端。示例:

for (const keyword of keywords) {
  const result = await sql.query(
     "SELECT Id from Keyword Where Word = ?",
     [keyword]
  );
}

您可以使用mysql2/promise包中的mysql2来使用承诺的mysql函数。

答案 2 :(得分:0)

仅forEach就无法做到这一点。每次sql.query的调用都将在循环迭代时尽快触发,并且不会等到完成。另外,也不能保证这些查询将按您调用它们的顺序解决。

至少可以使用回调函数,但这将是一些非常丑陋和困难的代码。 https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming)

剩下Promises和async / await。我强烈建议您花一些时间在这个主题上,因为您会经常遇到它

https://javascript.info/async

答案 3 :(得分:0)

代替寻找异步/循环解决方案,您可以使用一个SQL查询解决原始问题:

const args = keywords.map(_ => "?").join();
sql.query("Select Id, Word from Keyword Where Word in (" + args + ")", keywords, 
    function (err, records) {...}
);

答案 4 :(得分:0)

我认为异步js库(https://caolan.github.io/async)是解决与您的问题类似的东西的不错选择,并且使用此库,您可以得到更简洁的代码,而无需嵌套的异步调用来产生厄运金字塔。每当您遇到具有许多异步调用(并行或同步运行)的问题时,都可以使用它。 如文档所述,您只能使用诸如eachSeries之类的系列方法同时运行一次异步操作。

async.eachSeries(keywords, function(keyword, callback) {

 sql.query("Select Id from Keyword Where Word= ?", [keyword], 
    function (err, ids) {
        if (err){
           //if you want to stop reminding queries call callback with err
           callback(err)
        } else {
           callback();
        }
    }
 }, function(err) {
    // if any of the queris produced an error, err would equal that error
    if( err ) {
        // One of the iterations produced an error.
        // All processing will now stop.
    console.log('A query failed to process');
    }else {
        console.log('All queries have been processed successfully');
 }
});

答案 5 :(得分:-1)

forEach对异步功能没有任何特殊的作用。它会调用一个异步函数,但不会等到它完成后再继续。因此,如果需要顺序调用异步promise,则需要另一种方法。

尽管您未使用返回承诺的库,所以您需要将调用包装在承诺中(util.promisefy可以帮助您解决,或者您可以手动完成),或者您可以使用确实返回承诺的库版本。可以通过回调来做到这一点,但是要困难得多。

最直接的方法是在异步函数的侧面使用for … of循环。为此,您需要一个足够新的javascript版本,并且异步函数相当新。基本思路如下:

async function processCollection(collection) {
  for (let item of collection) {
    await processItem(item)
  }
}

processItem是另一个异步函数或返回诺言的函数(基本上是同一件事,只是语法不同)。

另一种不需要异步功能的方法是将诺言链接在一起。可以通过以下功能方式完成此操作:

collection.reduce((previousPromise, item) => {
  return previousPromose.then(() => processItem(item))
}, Promise.resolve(null))

(同样,processItem是一个返回诺言的函数)

这里要说的是,通过使用reduce,您基本上为集合中的每个项目调用一次promise.then(…).then(…).then(…)…。如果对then的回调返回了一个Promise,那么它将在调用下一个then回调之前等待该Promise完成。这样您就可以顺序执行了。同样,任何承诺拒绝也会传播,因此错误将阻止发生下一个调用。