我试图在下面的代码中控制执行流程,这意味着我希望它是串行的。
我正在从我的数据库读取和更新数据,并且我希望以正确的顺序发生这种情况。下面是我调用我的数据库的函数,查询函数包含在回调函数中。
我对承诺很新,所以也许错误可能是我忽略的愚蠢。如果您需要任何问题,请这样做。
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
我在想,也许我调用查询的方式是异步的,而且会弄乱一切。
答案 0 :(得分:1)
您正在使用for循环来运行异步任务并返回由它们修改的数组。但是因为它们是异步的,所以返回在它们完成之前发生。相反,您可以创建一个promises数组,其中每个promise是完成任务后解析的异步任务之一。要等到每个任务完成,您可以使用promises数组调用Promise.all
,这将返回一个使用已解析结果数组解析的promise。
对于第一个.then
,您可以使用Array.prototype.map
轻松创建一系列承诺。数组中的每个项都需要返回一个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
中的所有承诺都得到满足,就会执行下一个.then
来电。它将接收结果数组作为函数的第一个参数。这意味着你可以在那里使用它们。第二个get
与第一个map
类似,不同之处在于您不想在每个项目上调用array3
。在这种情况下.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);
});
})
不适用,因此for循环只会创建一个promise并将其添加到promises数组中。在使用.then
存储要更新的结果之前,但使用promises,您并不真正需要它。在这种情况下,您只需concat
两个数组的结果。
.then
这将返回一个使用连接数组解析的promise,因此您将所有结果(来自.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
使用超时来模拟异步调用)
update
现在代码按预期工作,但有许多改进使它更容易理解,也更方便。
要简化代码,您可以更改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
不需要是一个承诺,也不需要回调,因为它是同步的。
.catch
现在,您可以在用于创建新承诺的任何地方使用.then
。注意:我在发生错误时添加了拒绝案例,您必须在承诺上使用Promise.resolve(true)
来处理它们。
仍有太多不必要的.then
电话。首先.then
是没用的,因为你可以直接返回第一个get
调用的承诺。在你的例子中所做的就是自动将结果包装在一个承诺中。
您还使用两个get
调用来创建结果数组。不仅如此,它们执行的呼叫完全相同,即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
承诺的数组,然后等待所有承诺完成。
console.log("1")
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"));
正文已从32行代码(不包括{{1}}等)减少到5。
可运行代码段
{{1}}