node.js - 使用 Promise 控制流程无法正常工作

标签 node.js database promise

我试图在下面的代码中控制执行流程,这意味着我希望它是串行的。

我正在从我的数据库读取和更新数据,并且我希望它以正确的顺序发生。下面是我从中调用数据库的函数,查询函数包含在回调中。

我对 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 Promiseget 的回调结果解决.

.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/

相关文章:

javascript - 硬编码解析时间造成不必要的延迟

javascript - nodejs : event. 是异步的吗?

mysql - 如何从两个数据库查询数据并按范围对结果进行分组

database - 初始化对象或直接从数据库读取/写入数据库效率更高吗?

Python - 减少数据库的大小

javascript - 使用自定义错误退出/违背 promise

javascript - 如何在 OpenLayers 5 中删除绘图?

node.js Azure Web App 在长 url 上返回 400

node.js - 运行 jasmine 规范后关闭快速服务器

javascript - 如果没有解决,如何取消最后的 promise ?