使用promises来控制流量不能正常工作

时间:2017-03-26 23:48:37

标签: node.js database 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

我在想,也许我调用查询的方式是异步的,而且会弄乱一切。

1 个答案:

答案 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}}