我有一些node.js代码,它从循环中的API获取数据并运行mutliple mysql查询来更新某些行。
我遇到的问题是脚本一直运行,直到我终止与connection.end()
的mysql连接。我是异步代码的新手。我在哪里调用终止函数,以便在所有查询完成执行后执行?什么是正确的设计模式? waterfall会有什么好处吗?
这是我目前的代码片段(为简单起见,删除了错误处理):
var connection = mysql.createConnection({ host, user, etc... });
for (var i = 0; i < 10; i++) {
var url = "http://api.com?i="+i;
request(url, function(error, response, body) {
var data = JSON.parse(body);
for (el in data) {
connection.query(
"UPDATE table SET col = ? WHERE symbol = ?",
[
data[el].col,
el
]
);
}
});
}
// this will run before all queries have executed
// resulting in an error
connection.end();
答案 0 :(得分:1)
所以,这里的问题是你通过这里的数据同步循环:
writeln/1
而write/1
模块以回调样式处理查询:
nl
其中var data = JSON.parse(body);
for (el in data) {
connection.query(
"UPDATE table SET col = ? WHERE symbol = ?",
[
data[el].col,
el
]
);
}
具有签名mysql
,因此您可以通过这种方式处理结果,使其具有可重用的功能:
connection.query(query, function(error, rows, fields) {
if (error) {
return callback(error);
} else {
return callback(null,rows);
}
});
您可以在代码中调用
callback
也就是说,您必须考虑对数据库进行多次查询,并且不建议在for循环中执行此操作。你应该考虑使用一个更好的解决方案,就像你说的那样,或者使用Promise范例的承诺。
假设有这个好的功能:
callback(error,rows)
将一个项目数组和一个执行函数 var executeQuery = function(query,callback) {
var self=this;
this.connection.query(query, function(error, rows, fields) {
if (error) {
return callback(error);
} else {
return callback(null,rows);
}
});
}
作为输入,该函数具有Promise的解析和拒绝功能,所以让我们将你的executeQuery(statement, function(error,rows) {
//...
})
函数转换为Promise:< / p>
var promiseAllP = function(items, block) {
var promises = [];
items.forEach(function(item,index) {
promises.push( function(item,i) {
return new Promise(function(resolve, reject) {
return block.apply(this,[item,index,resolve,reject]);
});
}(item,index))
});
return Promise.all(promises);
}
现在,您可以以完全异步的方式处理数据:
function(item,index,resolve,reject)
executeQuery
将帮助您准备声明
var executeQueryP = function(query) {
var self=this;
return new Promise(function(resolve, reject) {
self.connection.query(query, function(error, rows, fields) {
if (error) {
return reject(error);
} else {
return resolve(null,rows);
}
});
}
这就是我们在这里所做的:
另请注意箭头函数语法promiseAllP(data,(item,index,resolve,reject) => {
var query= "UPDATE table SET col = %s WHERE symbol = %s";
// example: prepare the query from item in the data
query = replaceInString(query,item.col,item);
executeQueryP(query)
.then(result => resolve(result))
.catch(error => reject(error))
})
.then(results => { // all execution completed
console.log(results)
})
.catch(error => { // some error occurred while executing
console.error(error)
})
,它简化了编写函数的方式,这对Promise范例有很大帮助。
答案 1 :(得分:0)
对于任何有兴趣的人,我最终通过混合的承诺和计算查询来解决它,这与此类似(不确定这段代码是否真的有效,但想法就在那里):
function fetchFromAPI() {
return new Promise((resolve, reject)=>{
var urls = [];
for (var i = 0; i < 10; i++) {
urls.push("http://api.com?i="+i);
}
var data = [];
var requestedUrls=0;
urls.forEach(url=>{
request(url, (err, response, body) {
if(err) reject(err);
data.push(JSON.parse(body));
requestedUrls++;
if(requestedUrls==urls.length) resolve(data);
};
});
}
}
fetchFromAPI().then(data=>{
mysql.createConnection({ user, hostname, etc... });
var processedKeys=0;
data.forEach(el=> {
mysql.query("UPDATE table SET name = ? WHERE id = ?", [el.name, el.id], (err, rows, fields) => {
processedKeys++;
if(processedKeys==data.length) {
connection.end();
}
});
}
}).catch(err=>{
console.error(err);
});