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

Javascript array.forEach()如何处理异步函数?,javascript,node.js,Javascript,Node.js,我试图迭代一组关键字,并为每一个关键字调用mysql.query() 问题是,我需要在下一个查询调用开始之前结束每个查询调用。我如何做到这一点 我尝试过让它与承诺请求一起工作,但我并不完全理解异步调用是如何工作的 keywords.forEach(keyword => { sql.query("Select Id from Keyword Where Word= ?", [keyword], function (err, ids) {...} })

我试图迭代一组关键字,并为每一个关键字调用mysql.query()

问题是,我需要在下一个查询调用开始之前结束每个查询调用。我如何做到这一点

我尝试过让它与承诺请求一起工作,但我并不完全理解异步调用是如何工作的

keywords.forEach(keyword => {
    sql.query("Select Id from Keyword Where Word= ?", [keyword], 
                function (err, ids) {...}
});
forEach
不会等待异步功能完成。您需要传递回调的原因是,这是您在完成后必须执行的唯一真正方式

因此,
forEach
根本不起作用。可以重写它以使第二次迭代异步进行,但这非常难看

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

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

您可以从
mysql2
包中使用
mysql2/promise
来使用promisified mysql函数。

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

看起来,虽然您没有使用返回承诺的库,但是您需要将调用包装在承诺中(
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(…)…
。如果对
then
的回调返回一个承诺,那么它将等待该承诺完成,然后再调用下一个
then
回调。所以你得到了顺序执行。此外,任何承诺拒绝也会传播,因此错误将阻止下一次调用的发生。

仅使用forEach无法做到这一点。每次调用sql.query都会在循环迭代时快速启动,不会等到它完成。此外,也不能保证这些查询将按照您调用它们的顺序进行解析

至少,您可以使用回调函数,但这将是一些非常难看且难以处理的代码

这就留下了承诺和异步/等待。我强烈建议你在这个话题上花点时间,因为你会经常碰到它


你可以反复做

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)

您可以通过一个SQL查询解决原始问题,而不是寻找异步/循环解决方案:

const args = keywords.map(_ => "?").join();
sql.query("Select Id, Word from Keyword Where Word in (" + args + ")", keywords, 
    function (err, records) {...}
);
我认为asyncjslibrary()是解决类似问题的一个很好的选择,使用这个库,您可以获得更清晰的代码,而不必使用嵌套的异步调用来产生金字塔。每当您遇到一个问题时,有许多异步调用将并行运行或同步运行,您都可以使用它。 正如文档中所说,使用诸如eachSeries之类的series方法,一次只能运行一个异步操作

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');
 }
});

forEach
是同步的,因此在其回调内部启动异步工作不会有多大帮助。请考虑使用<代码>允诺。所有(关键词:map(…))< />代码以获得所有数据都能解决的承诺。如果要进行串行调用,则必须在前一次调用的回调中做出下一步,等等。承诺在这方面会有所帮助,但方法是一样的。@CollinD:这仍然会并行运行请求。你能详细说明一下并给我一个例子吗,因为我很难理解它是如何工作的?至于问题的来源:你应该编写一个SQL查询,为每个感兴趣的ID提供结果(例如,在操作符中使用
)。哇,我第一次被否决:)。我猜这就是我在完成回答之前发布的内容。现在更新了替代方案和更多信息。