蓝鸟承诺和别名

Bluebird promise and alias

有没有办法传入承诺数组,以便您可以通过给定的别名而不是索引来访问结果?不使用道具。

喜欢这样的东西:

var a = [{shop: promise(customer)},{customer:promise(customer)}]

Promise.all(a).then(function(res){
    console.log(res.shop):
});

Props 可以用于此目的,但它不符合我的要求,但如果有人需要它,这里是如何使用 props 完成的:

Promise.props({
    pictures: getPictures(),
    comments: getComments(),
    tweets: getTweets()
}).then(function(result) {
    console.log(result.tweets, result.pictures, result.comments);
});

Promise.all() 仅适用于数组,但适用于 ES6:

const a = [promise(customer), promise(customer)];
Promise.all(a)
  .then(([shop, customer]) => console.log(shop))

或者,或者,使用 Bluebird 的 Promise.prototype.spread()

var a = [promise(customer), promise(customer)];
Promise.all(a)
  .spread(function(shop, customer) {
    console.log(shop);
  });

您可以创建自己的 .all() 版本,它可以满足您的要求,如下所示:

Promise.allProp = function(arrayOfObjects) {
    var promises, keys = [];
    arrayOfObjects.forEach(function(item) {
        var key = Object.keys(item)[0];      // use first property only
        promises.push(item[key]);
        keys.push(key);
    });
    return Promise.all(promises).then(function(results) {
        // combine results array back with keys
        var obj = {};
        return results.forEach(function(r, index) {
            obj[keys[index]] = r;
        })
        return obj;
    });
}

你向它传递一个对象数组,例如

[{shop:  funcAPromise()}, {customer: funcAPromise()}]

并且,结果承诺的解析值将是一个对象,每个 属性 名称都有一个结果。

{shop:  funcAResolvedValue, customer: funcBResolvedValue}

用法:

var array = [{shop:  funcAPromise()}, {customer: funcAPromise()}];
Promise.allWithProps(array).then(function(results) {
   console.log(results);   // {shop:  funcAResolvedValue, customer: funcBResolvedValue}
});

或者,如果您已经在使用 Bluebird,修改输入数组以适应 Promise.props() 需要并直接使用它可能会更容易:

Promise.allProp = function(arrayOfObjects) {
    var obj = {};
    arrayOfObjects.forEach(function(item) {
        var key = Object.keys(item)[0];
        obj[key] = item[key];
    });
    return Promise.props(obj);
}

如果扩展 Promise 对象让您感到困扰,并且您已经拥有 Bluebird,那么您可以创建一个辅助函数,将您的对象数组修改为 Promise.props() 想要的单个对象:

 function convertToObject(array) {
    var obj = {};
    array.forEach(function(item) {
        var key = Object.keys(item)[0];
        obj[key] = item[key];
    });
    return obj;
 }

 Promise.props(convertToObject(myInputArrayOfObjects)).then(function(r) {
     console.log(r.tweets);
     console.log(r.comments);
 });

注意:这些解决方案都假设您不会对同一个 属性 名称有多个承诺,并且每个传入对象只有一个 属性 名字。如果多个对象具有相同的 属性 名称,则行为会略有不同。

第一个 Promise.allProp() 实际上会等待所有承诺,但 return 只有最后一个的解决结果。

第二个 Promise.allProp()convertToObject() 解决方案将只等待最后一个冲突的承诺,如果有任何冲突和 return 最后解决的结果。

如果需要,可以更改前两个解决方案中的任何一个以包含传入对象的所有属性(而不仅仅是第一个 属性)。


更通用的实现

这里有一些更通用的解决方案,可以对输入 属性 冲突进行错误检查。这适用于通用的 ES6 promises 并取代 Bluebird 的 Promise.props() 功能,因为这将接受具有属性列表的单个对象或每个具有属性列表的对象数组。

它将处理所有对象上的所有 property/value 对。如果它看到给定的 属性 名称出现在多个对象上,它将抛出异常 - 它们必须都是唯一的,因为这是将结果返回到具有 property/value 对的单个对象中的唯一方法,其中value 是传入承诺的解析值。

这是更通用的实现。

// Takes a single object or an array of objects 
//   where each object has one or more keys/value pairs
// The values are all promises which should be combined and waited on with Promise.all()
// The keys must all be unique across all the objects in the array,
//   the same key cannot appear in any other object
// The resolved value is a single object with key/value pairs where the value is the
//     resolved value of all the incoming promises
Promise.allProp = function(obj) {
    try {
        var arrOfObjErr = "Must pass an object or array of objects to .allProp()";
        if (typeof obj !== "object") {
            throw new Error(arrOfObjErr);
        }
        // if just a single object was passed, then put it in an array so the general array processing code works
        var arr = Array.isArray(obj) ? obj : [obj];
        var promises = [], keys = [], keyMap = {};
        arr.forEach(function(item) {
            if (typeof item !== "object") {
                throw new Error(arrOfObjErr);
            }
            Object.keys(item).forEach(function(key) {
                if (keyMap[key]) {
                    throw new Error("Conflicting property name '" + key + "' - all property names must be unique");
                } else {
                    // show key has already been used
                    keyMap[key] = true;
                    // save promise and key in separate arrays
                    promises.push(item[key]);
                    keys.push(key);
                }
            });
        });
    } catch(e) {
        // turn any synchronous exceptions into a rejection so the caller doesn't have to use try/catch
        return Promise.reject(e);
    }
    // await all promises, then combine resolved results with original keys into a single object
    return Promise.all(promises).then(function(results) {
        // combine results array back with keys
        var obj = {};
        results.forEach(function(result, index) {
            obj[keys[index]] = result;
        })
        // resolve with a single object full of key/value pairs
        return obj;
    });
}

在实现中,它遍历了传入的所有对象,并收集了一个单独的承诺数组和一个对应的键数组。使用 Promise.all() 等待 promise 数组,然后当这些 promise 全部完成时,它将每个 promise 的解析结果处理回具有原始 属性 名称的对象。最终解析的结果是一个对象,上面有 property/value 对。

如果发现任何冲突的键,它将拒绝并显示一条描述性错误消息。

// example of single object
Promise.allProp({tweets: p1, comments: p2}).then(function(result) {
    console.log(result.tweets);
    console.log(result.comments);
}, function(err) {
    console.log(err);
});

// example of array of objects
Promise.allProp([{tweets: p1, comments: p2}, {pictures: p3}]).then(function(result) {
    console.log(result.tweets);
    console.log(result.comments);
    console.log(result.pictures);
}, function(err) {
    console.log(err);
});

// example of illegal same property appearing on more than one object
Promise.allProp([{tweets: p1, comments: p2}, {tweets: p3, pictures: p4}, ]).then(function(result) {
    console.log(result.tweets);
    console.log(result.comments);
    console.log(result.pictures);
}, function(err) {
    // will reject because of "tweets" property appearing on more than one object
    console.log(err);
});

代码的测试用例:https://jsfiddle.net/jfriend00/uamxax4e/