bluebird 承诺每一个都不是 return 最后的结果

bluebird promise each not return the last result

我正在使用 promise.each 循环使用 bookshelfjs 的 2 db 查询,但是角色没有给我结果 -> roles[resource.get('name')] = role.get('name');,但它给了我对象来自 slect 语句:

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;

来自 Promise.each() 的 Bluebird 文档:

Resolves to the original array unmodified, this method is meant to be used for side effects.

所以,这意味着当你这样做时:

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

val 将是您传入的原始数组,而不是您的 roles 对象。

因为你的 roles 对象在更高的范围内,你可以在你的 .then() 处理程序中删除声明为 roles 的参数并直接引用更高范围的 roles变量。

您还应该避免使用此处的 promise constructor anti-pattern 并且不要创建新的承诺,而只是 return 您已经拥有的承诺。

你可以这样做:

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

这个 returned 承诺的解析值将是您的 roles 对象。

变更摘要:

  1. Return 来自 Promise.each() 的承诺,而不是创建新的承诺(无需在此处创建新的承诺)。
  2. 让拒绝向上传播,无需手动拒绝更高级别的承诺。
  3. Promise.each().then() 处理程序中,删除名为 roles 的声明参数,因为它与更高范围的 roles 对象冲突。
  4. Return roles 在那个 .then() 处理程序中,所以它成为你正在 returning 的承诺的解析值。
  5. 删除内部 .catch(),因为外部 .catch() 也可以完成它的工作。
  6. 更改 .catch() 以抛出重新打包的错误值。
  7. 移除内部的 return roles;,因为它不需要。 roles 对象在更高范围内始终可用,因此无需使其成为这些内部承诺的解析值(事实上,这样做可能只会造成混乱)。

可能的性能优化。由于 none 的结果取决于先前的结果,看来您可以 运行 所有异步操作并行而不是串行,在这种情况下,您可以将 Promise.each() 替换为 Promise.map().

是的,抱歉,我们改变了主意。

在 3.x 中,我们应该使 .each return 结果而不是原始值,但这对很多人来说破坏了代码,因此我们将其改回并添加一个 mapSeries.

基本上,只要您使用 each 并关心结果,就使用 .mapSeries,它完全符合您的期望。如果您可以 运行 这些项目同时使用 .map,因为它可能会表现得更好。