我想为每个循环执行一次,但让它同步运行。循环的每次迭代都将执行一次http.get调用,并返回json,以便将值插入数据库。问题是for循环以异步方式运行,导致所有http.gets都立即运行,我的数据库最终没有插入所有数据。我正在使用async-foreach尝试做我想做的事情它可以做,但如果我能以正确的方式做到这一点,我就不必使用它。
mCardImport = require('m_cardImport.js');
var http = require('http');
app.get('/path/hi', function(req, res) {
mCardImport.getList(function(sets) {
forEach(sets, function(item, index, arr) {
theUrl = 'http://' + sets.set_code + '.json';
http.get(theUrl, function(res) {
var jsonData = '';
res.on('data', function(chunk) {
jsonData += chunk;
});
res.on('end', function() {
var theResponse = JSON.parse(jsonData);
mCardImport.importResponse(theResponse.list, theResponse.code, function(theSet) {
console.log("SET: " + theSet);
});
});
});
});
});
});
和我的模特
exports.importResponse = function(cardList, setCode, callback) {
mysqlLib.getConnection(function(err, connection) {
forEach(cardList, function(item, index, arr) {
var theSql = "INSERT INTO table (name, code, multid, collector_set_num) VALUES "
+ "(?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id";
connection.query(theSql, [item.name, setCode, item.multid, item.number], function(err, results) {
if (err) {
console.log(err);
};
});
});
});
callback(setCode);
};
答案 0 :(得分:46)
使用递归代码非常干净。等待http响应返回然后触发下一次尝试。这适用于所有版本的节点。
var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];
var processItems = function(x){
if( x < urls.length ) {
http.get(urls[x], function(res) {
// add some code here to process the response
processItems(x+1);
});
}
};
processItems(0);
使用promises的解决方案也可以正常运行,而且更简洁。例如,如果你有一个version of get that returns a promise和Node v7.6 +,你可以编写一个async / await函数,就像这个例子一样,它使用了一些新的JS特性。
const urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];
async function processItems(urls){
for(const url of urls) {
const response = await promisifiedHttpGet(url);
// add some code here to process the response.
}
};
processItems(urls);
注意:这两个示例都跳过了错误处理,但您可能应该在生产应用程序中使用它。
答案 1 :(得分:5)
要循环并同步链接异步操作,最干净的解决方案可能是使用promise库(在ES6中引入了承诺,这是可行的方法)。
使用Bluebird,这可能是
Var p = Promise.resolve();
forEach(sets, function(item, index, arr) {
p.then(new Promise(function(resolve, reject) {
http.get(theUrl, function(res) {
....
res.on('end', function() {
...
resolve();
}
}));
});
p.then(function(){
// all tasks launched in the loop are finished
});
答案 2 :(得分:2)
我发现在完成每次调用后我没有释放我的mysql连接,这会导致连接失败并导致同步问题。
显式调用connection.release();
后,即使以异步方式,我的代码也能正常工作。
感谢那些发布此问题的人。
答案 3 :(得分:0)
"use strict";
var Promise = require("bluebird");
var some = require('promise-sequence/lib/some');
var pinger = function(wht) {
return new Promise(function(resolve, reject) {
setTimeout(function () {
console.log('I`ll Be Waiting: ' + wht);
resolve(wht);
}, Math.random() * (2000 - 1500) + 1500);
});
}
var result = [];
for (var i = 0; i <= 12; i++) {
result.push(i);
}
some(result, pinger).then(function(result){
console.log(result);
});
答案 4 :(得分:0)
只需将循环包装在async
函数中即可。这个例子说明了我的意思:
const oneSecond = async () =>
new Promise((res, _) => setTimeout(res, 1000));
此功能仅需1秒钟即可完成:
const syncFun = () => {
for (let i = 0; i < 5; i++) {
oneSecond().then(() => console.log(`${i}`));
}
}
syncFun(); // Completes after 1 second ❌
这一项工作正常,在5秒钟后完成:
const asyncFun = async () => {
for (let i = 0; i < 5; i++) {
await oneSecond();
console.log(`${i}`);
}
}
asyncFun(); // Completes after 5 seconds ✅
答案 5 :(得分:-1)
var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];
for (i = 0; i < urls.length; i++){
http.get(urls[i], function(res) {
// add some code here to process the response
});
}