我试图在下面的代码中控制执行流程,这意味着我希望它是串行的。
我正在从我的数据库读取和更新数据,并且我希望它以正确的顺序发生。下面是我从中调用数据库的函数,查询函数包含在回调中。
我对 promise 很陌生,所以也许这个错误可能是我忽略的一些愚蠢的东西。如果你有什么需要问的,请这样做。
function my_function(array, array2)
{
var array3 = [];
return Promise.resolve(true)
.then(function()
{
console.log("1")
for(var i=0; i< array.length; i++)
{
get(array[i], function(results){
console.log("2")
array3.push(..);
});
}
return array3;
}).then(function()
{
console.log("3")
for(var i=0; i< array2.length; i+=2)
{
//...
get(array2[i], function(results){
console.log("4")
return array3.push(...);
});
}
return array3;
}).then(function(array3)
{
console.log("5")
for(var i=0; i<array3.length; i++)
{
get(array3[i], function(results){
console.log("6")
update(.., function(callb_result){
return;
});
});
}
});
}
这是我调用查询的方式。
function get(array, callback)
{
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
...
return callback(something);
});
}
function update(.., callback)
{
db.run(`UPDATE .. SET ...`);
return callback("updated"); //I dont want to return anything
}
日志中打印的内容
1
3
5
2
4
6
我在想也许我调用查询的方式是异步的,这把一切都搞砸了。
最佳答案
您正在使用 for 循环来运行异步任务并返回一个由它们修改的数组。但是因为它们是异步的,所以返回发生在它们完成之前。相反,您可以创建一个 promise 数组,其中每个 promise 都是异步任务之一,一旦任务完成就会解决。要等到每项任务完成,您可以调用 Promise.all
使用 Promise 数组,它返回一个使用已解析结果数组解析的 Promise。
对于第一个 .then
您可以使用 Array.prototype.map
轻松创建一系列 promise 。数组中的每一项都需要返回一个 new Promise
由 get
的回调结果解决.
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(result) {
console.log("2");
resolve(result);
});
});
});
return Promise.all(promiseArray);
})
当您返回
Promise.all
下一个.then
一旦 promiseArray
中的所有 promise 执行一次调用得到满足。它将接收结果数组作为函数的第一个参数。这意味着您可以在那里使用它们。第二个.then
和第一个类似,只是你不想调用get
在每个项目上。在这种情况下 map
不适用,所以 for 循环只会创建一个 Promise 并将其添加到 Promise 数组中。使用前array3
存储您想要更新的结果,但 promise 您并不真正需要它。在这种情况下,您可以简单地 concat
两个数组的结果。.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
// Wait for all promises to be resolved
// Then concatenate both arrays of results
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
这将返回一个通过连接数组解析的 promise ,因此您将拥有所有结果(来自两个
.then
调用)作为一个数组,该数组将传递给下一个 .then
功能。在第三个也是最后一个 .then
您只需调用update
在数组的每个元素上。您无需调用get
再次,因为您已经这样做了,并且您传递了结果。.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i], function(result) {
console.log(result);
});
}
});
完整的可运行代码 (
get
使用超时来模拟异步调用)function myFunction(array, array2) {
return Promise.resolve(true)
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(results) {
console.log("2");
resolve(results);
});
});
});
return Promise.all(promiseArray);
})
.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i]);
}
});
}
function get(item, cb) {
// Simply call the callback with the item after 1 second
setTimeout(() => cb(item), 1000);
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const array = ["arr1item1", "arr1item2", "arr1item3"];
const array2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(array, array2);
改进代码
代码现在可以按预期工作,但有许多改进使其更容易理解并且更方便也更短。
为了简化代码,您可以更改
get
函数返回一个 promise 。这让它变得容易多了,因为您不需要在每一步都创建一个 Promise。和update
不需要是 promise ,也不需要回调,因为它是同步的。function get(array) {
return new Promise(function(resolve, reject) {
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
if (err) {
return reject(error);
}
resolve(something);
});
});
}
现在您可以使用
get
你曾经在任何地方创造一个新的 promise 。注意:我在出现错误时添加了拒绝案例,您必须使用 .catch
处理它们在 promise 上。还是有太多没用的
.then
来电。首先Promise.resolve(true)
没用,因为你可以只返回第一个 .then
的 promise 直接打电话。它在您的示例中所做的只是将结果自动包装在一个 promise 中。您还使用了两个
.then
调用以创建结果数组。不仅如此,它们还执行完全相同的调用,即 get
.目前,您也可以等到第一组完成后再执行第二组,但它们可以同时执行。相反,您可以创建一个包含所有 get
的数组。 promise ,然后等待他们全部完成。function myFunction(array, array2) {
// array.map(get) is equivalent to array.map(item => get(item))
// which in turn is equivalent to:
// array.map(function(item) {
// return get(item);
// })
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
myFunction
正文已从 32 行代码(不包括 console.log("1")
等)减少到 5 行。可运行片段
function myFunction(array, array2) {
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
function get(item) {
console.log(`Starting get of ${item}`);
return new Promise((resolve, reject) => {
// Simply call the callback with the item after 1 second
setTimeout(() => resolve(item), 1000);
});
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const testArr1 = ["arr1item1", "arr1item2", "arr1item3"];
const testArr2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(testArr1, testArr2).then(() => console.log("Updated all items"));
关于node.js - 使用 Promise 控制流程无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43035946/