您可以创建自己的.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()}]
而且,结果 promise 的解析值将是一个对象,每个属性名称都有一个结果。
{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() 实际上会等待所有的promise,但只返回最后一个的resolved结果。
第二个Promise.allProp() 和convertToObject() 解决方案只会等待最后一个冲突的promise(如果有任何冲突)并返回最后一个解决的结果。
如果需要,前两个解决方案中的任何一个都可以更改为包含传入对象的所有属性(不仅仅是第一个属性)。
更通用的实现
这里有一个更通用的解决方案,对输入属性冲突进行错误检查。这适用于通用 ES6 承诺并取代 Bluebird 的 Promise.props() 功能,因为这将接受具有属性列表的单个对象或每个具有属性列表的对象数组。
它将处理所有对象上的所有属性/值对。如果它看到给定的属性名称出现在多个对象上,它将引发异常 - 它们都必须是唯一的,因为这是将结果返回到具有属性/值对的单个对象中的唯一方法,其中 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数组和一个对应的key数组。使用Promise.all() 等待承诺数组,然后当这些承诺全部完成时,它将每个承诺的解析结果处理回具有原始属性名称的对象。最终解析的结果是一个带有属性/值对的对象。
如果发现任何冲突的键,它将拒绝并显示描述性错误消息。
// 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/