【问题标题】:Save value of then in a variable outside of the Promise将 then 的值保存在 Promise 之外的变量中
【发布时间】:2019-10-14 07:15:51
【问题描述】:

我对 Promises 的概念还很陌生,我正在尝试了解作用域的工作原理。我基本上是在尝试将 then() 内部的值存储到 Promise

之外的变量中

下面是我用 Nodejs (Express) 编写的一个简单函数,使用 Sequelize 在 DB 上运行查询。

exports.getTest = (req, res, next) => {
    var categories = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    for (var i = 0; i < names.length; i++) {
        model.Category.findOne({
            where: {
                name: names[i]
            },
            attributes: ['id']
        }).then(id => {
            categories.push(
            {
                category_id: id.id
            });
        });
    }
    res.json(categories);
}

在那之后我还有其他逻辑要运行,并且我有一个围绕 Promise 的 for 循环。所以,我不能在 then 中运行我的下一个逻辑,否则我会因为 for 循环而让它运行多次。我需要填充数组 categories 以在我的下一个操作中使用它。

目前,我的回复 (res.json(categories)) 是 []

任何帮助将不胜感激。

PS:我知道这是一个常见的话题,但正如我所提到的,我对此很陌生,其他答案不适合我的情况,让我更加困惑。

提前致谢!

【问题讨论】:

  • 你试过使用 async/await 吗?

标签: javascript node.js express promise sequelize.js


【解决方案1】:

你可以试试 Promise.all()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Promise.all() 方法返回一个 Promise,当所有作为可迭代对象传递的承诺都已解决或可迭代对象不包含任何承诺时,该 Promise 将解决。它以第一个拒绝的承诺的原因拒绝。

var getTest = (req, res, next) => {
    var categories = [];
    var promises = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    var resolveCount = 0;
    for (var i = 0; i < names.length; i++) {
        // Store the name in variable so that can be persistent and not
        // affected by the changing 'i' value
        const name = names[i]
        promises.push(new Promise((resolve, reject) => {
        
          // Your DB calls here. We shall use a simple timer to mimic the
          // effect
          setTimeout(() => {
            categories.push(name)
            resolveCount++;
            resolve();
          }, 1000)
        }));
    }
    
    
    Promise.all(promises).then(function() {
      console.log("This should run ONCE before AFTER promise resolved")
      console.log("resolveCount: " + resolveCount)
      console.log(categories);
      
      // Do your logic with the updated array
      // res.json(categories);
    });
    
    console.log("This will run immediately, before any promise resolve")
    console.log("resolveCount: " + resolveCount)
    console.log(categories)
}

getTest();

【讨论】:

  • 嘿!非常感谢。这非常有效。艾萨克的解决方案也是如此。但是,您已将 DB 调用封装到另一个 Promise new Promise((resolve, reject) 中。在 Isaac 的解决方案中,我直接将 DB 调用(Promises)添加到 promises 数组中。它有什么不同?不过,结果是完全相同的。
  • 函数model.Category.findOne(..).then()返回一个Promise,所以你可以把这个函数的结果压入数组。不过,对于我的示例,我创建了多个新的 Promise 来说明总体思路。
【解决方案2】:
exports.getTest = (req, res, next) => {
    var categories = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    names.forEach(name => {
        Category.findOne({where: {name: name}}).then(category => {
            categories.push({category_id: category.id})
        })
    })
    res.json(categories);
}

基本上,model.findeOne() 返回一个带有每个名称的第一类对象的承诺。 then() 捕获该承诺,解决它,然后给它一个回调函数,该函数将该对象作为参数传递。

它可能看起来像

Categories.findOne({where: {name: name}).then(function(category){
    // do something with that category
})

但是箭头函数使它更具可读性,因为 then(category => {//some 代码})。

【讨论】:

  • ForEach 的使用方式几乎相同,我们使用箭头函数代替categories.forEach(function(category){ //do something }),并且该数组中的每个类别都将作为参数传递给您的函数
  • 这根本不能解决问题,它几乎与 OP 的代码完全相同,只是你使用的是 .forEach 而不是使用 for 循环
【解决方案3】:

在您的情况下,categories 将始终返回 [],因为您无需等待所有承诺完成后才返回响应。 For 循环在继续下一次迭代之前不会等待异步操作完成。因此循环结束,并在其中任何一个完成之前返回响应。

您应该将它们推送到一个数组中,而不是在 for 循环中调用 Promise,然后您可以将其传递给 Promise.all() 函数。

它应该是这样的

exports.getTest = () => {
    var categories = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    var promiseArray = [];
    for (var i = 0; i < names.length; i++) {
        promiseArray.push(
          model.Category.findOne({
              where: {
                  name: names[i]
              },
              attributes: ['id']
          }).then(id => {
              categories.push(
              {
                  category_id: id.id
              });
          });
        )
    }

    return Promise.all(promiseArr)
}

getTest() 现在返回一个 promise,所以可以这样调用它

getTest()
  .then(data => {
    // data will be an array of promise responses
  }).catch(err => {
    console.log(err);
  })

【讨论】:

  • 嗨!非常感谢。这工作得很好。虽然您的回答很中肯,但吉米的回答帮助我理解了这个概念及其背后的流程。有了这种理解,我就能够修改代码以满足我的要求。不过非常感谢您的回答!
猜你喜欢
  • 2018-05-13
  • 1970-01-01
  • 2021-10-04
  • 1970-01-01
  • 1970-01-01
  • 2020-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多