Блюберд обещает каждому не вернуть последний результат

Я использую promise.each перебрать запрос 2 дБ с помощью bookshelfjs, но роли не дают мне этого результата -> roles[resource.get('name')] = role.get('name');, но это дает мне объект из утверждения выбора:

var promise = new Promise(
        function resolver(resolve, reject) {   
            var roles = {};
            Promise.each(user.relations.relates.models, function(relation){
                return Resource.forge({resourceId: relation.get('resourceId')}).fetch().then(function(resource){
                    return Role.forge({roleId: relation.get('roleId')}).fetch().then(function(role){
                        roles[resource.get('name')] = role.get('name');
                        return roles;
                    }).catch(function(err){
                        reject({"status":"error", "data": err});
                    });
                }).catch(function(err){
                    reject({"status":"error", "data": err});
                });
            }).then(function(roles){
              resolve(roles);
            });
        }
    );

    return promise;

2 ответа

Решение

Из документов Bluebird для Promise.each():

Разрешает исходный массив без изменений, этот метод предназначен для побочных эффектов.

Итак, это означает, что когда вы делаете:

Promise.each(array).then(function(val) {
   // iteration finished here
   // val is the original array
});

Тот val будет исходный массив, который вы передали, а не ваш roles объект.

Потому что ваш roles Объект находится в более высоком объеме, вы можете удалить параметр, объявленный как roles в вашем .then() обработчик и просто напрямую относится к более высокой области roles переменная.

Вам также следует избегать анти-паттерна конструктора обещаний и не создавать новое обещание, а просто вернуть то, которое у вас уже есть.

Вы можете сделать это так:

var roles = {};
return Promise.each(user.relations.relates.models, function (relation) {
    return Resource.forge({resourceId: relation.get('resourceId')}).fetch().then(function (resource) {
        return Role.forge({roleId: relation.get('roleId')}).fetch().then(function (role) {
            roles[resource.get('name')] = role.get('name');
        });
    }).catch(function (err) {
        // repackage how the error is presented into our own object
        throw ({"status": "error", "data": err});
    });
}).then(function () {
    // make the resolved value of the final promise be the roles object
    return roles;
});

Разрешенное значение этого возвращенного обещания будет вашим roles объект.

Сводка изменений:

  1. Верните обещание от Promise.each() вместо создания нового обещания (нет необходимости создавать новое обещание здесь).
  2. Пусть отклонения распространяются обратно, нет необходимости вручную отклонять обещание более высокого уровня.
  3. в .then() обработчик для Promise.each()удалите объявленный параметр с именем roles так как это конфликтует с вашей высшей областью roles объект.
  4. Вернуть roles в этом .then() обработчик, поэтому он становится решающим значением обещания, которое вы возвращаете.
  5. Удалить внутренний .catch() так как внешний .catch() может сделать свою работу тоже.
  6. Изменить .catch() бросить перепакованное значение ошибки.
  7. Удалить внутренний return roles; так как это не нужно. roles объект постоянно доступен в большем объеме, поэтому нет необходимости делать его разрешенным значением этих внутренних обещаний (на самом деле, это может создать путаницу).

Возможна оптимизация производительности. Поскольку ни один из ваших результатов не зависит от предыдущих результатов, кажется, что, возможно, вы могли бы выполнять все свои асинхронные операции параллельно, а не последовательно, и в этом случае вы могли бы заменить Promise.each() с Promise.map(),

Да, извините, мы передумали.

В 3.х мы должны были сделать .each возвращать результаты, а не исходные значения, но это сломало код для многих людей, поэтому мы изменили его и добавили mapSeries,

В основном, используйте .mapSeries всякий раз, когда вы используете each и заботиться о результатах, он делает именно то, что вы ожидаете. Если вы можете запустить элементы одновременно, используйте .map хотя, поскольку это, вероятно, будет работать намного лучше.

Другие вопросы по тегам