Сохраните значение затем в переменной за пределами Обещания - PullRequest
0 голосов
/ 28 мая 2019

Я довольно новичок в концепции Обещаний и пытаюсь понять, как работают эти области.Я в основном пытаюсь сохранить значение из then () в переменную вне Promise

Ниже приведена простая функция, которую я написал вNodejs (Express), использующий Sequelize для запуска запроса к БД.

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);
}

У меня есть другая логика для запуска после этого, и у меня есть цикл for вокруг Promise.Поэтому я не могу запустить свою следующую логику внутри функции then, иначе она будет запущена несколько раз из-за цикла for.Мне нужно заполнить массив category , чтобы использовать его в моей следующей операции.

В настоящее время мой ответ (res.json(categories)) []

Любая помощь будетпризнателен.

PS: я знаю, что это общая тема, но, как я уже говорил, я довольно новичок в этом, и другие ответы не соответствовали моему сценарию и еще больше сбивали меня с толку.

Заранее спасибо!

Ответы [ 3 ]

2 голосов
/ 28 мая 2019

В вашем случае categories всегда вернет [], потому что вы не дожидаетесь завершения всех своих обещаний, прежде чем вернуть свой ответ.Циклы for не ждут завершения асинхронных действий, прежде чем переходить к следующей итерации.Поэтому цикл заканчивается, и ответ возвращается до того, как завершится любой из них.

Вместо вызова обещаний в цикле for, вы должны передать их в массив, который затем можно передать в функцию 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() теперь возвращает обещание, поэтому его можно назвать вот так

getTest()
  .then(data => {
    // data will be an array of promise responses
  }).catch(err => {
    console.log(err);
  })
1 голос
/ 28 мая 2019

Вы можете попробовать Promise.all ()

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

Метод Promise.all () возвращает одно Обещание, которое разрешается, когда все обещания, переданные как итеративные, разрешены или когда итерация не содержит обещаний. Он отклоняет причину первого обещания, которое отклоняет.

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();
0 голосов
/ 28 мая 2019
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 () возвращает обещание с объектом первой категории с каждым из имен. Затем () перехватывает это обещание, разрешает его, и вы передаете ему функцию обратного вызова, которая передает этот объект в качестве параметра.

Это может выглядеть примерно так:

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

Но функция стрелки делает его намного более читабельным, чем тогда (category => {// some код}).

...