我正在尝试遍历关键字列表,并为每个关键字调用mysql.query()。
问题是,我需要每个查询调用在下一个查询开始之前结束。我该如何实现?
我曾尝试使其与Promise请求一起使用,但我不完全了解异步调用的工作原理。
keywords.forEach(keyword => {
sql.query("Select Id from Keyword Where Word= ?", [keyword],
function (err, ids) {...}
});
答案 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。我强烈建议您花一些时间在这个主题上,因为您会经常遇到它
答案 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完成。这样您就可以顺序执行了。同样,任何承诺拒绝也会传播,因此错误将阻止发生下一个调用。