我正在使用Promise.all()从多个端点获取数据。使用Promise.all()可以返回一个数组,您可以从该数组中析构项目,例如
const [fetch1, fetch2] = Promise.all(urls.map(url => fetch(url)))
我正在寻找一种使用配置对象动态命名那些解构项目(名称1和名称2)的方法。
const urls = [
{
name: 'google',
url: 'https://www.google.com'
},
{
name: 'bbc',
url: 'https://www.bbc.co.uk'
}
]
const [google, bbc] = Promise.all(urls.map(url => fetch(url.url)))
在上述示例中,从promise.all分解而来的项目应使用urls数组中的名称,即urls.name
基本上,我正在寻找一种在传递给promise.all()的配置对象中命名我的导出的方法>
主要原因是从这些承诺返回的数据将以urls配置对象中指定的名称进行键入。因此我们也可以这样做
const data = Promise.all(urls.map(url => fetch(url.url)))
console.log(data.google || data.bbc)
我正在使用Promise.all()从多个端点获取数据。使用Promise.all()返回一个数组,您可以销毁该数组中的项目,例如const [fetch1,fetch2] = ...
这应该做:
Object.fromEntries
首先:废弃Promise
构造函数this usage is an antipattern!
现在,到您的实际问题:正确识别后,您将丢失每个值的键。您将需要在每个Promise中传递它,以便在等待所有项目后可以重建对象:
function mapObjectToArray(obj, cb) {
var res = [];
for (var key in obj)
res.push(cb(obj[key], key));
return res;
}
return Promise.all(mapObjectToArray(input, function(arg, key) {
return getPromiseFor(arg, key).then(function(value) {
return {key: key, value: value};
});
}).then(function(arr) {
var obj = {};
for (var i=0; i<arr.length; i++)
obj[arr[i].key] = arr[i].value;
return obj;
});
更强大的库(例如Bluebird)也将其作为辅助功能,例如Promise.props
。
而且,您不应该使用该伪递归Promise.props
函数。您可以简单地将承诺链接在一起:
load
我实际上为此创建了一个库,并将其发布到github和npm:
….then(function (resources) {
return game.scripts.reduce(function(queue, script) {
return queue.then(function() {
return getScript(root + script);
});
}, Promise.resolve()).then(function() {
return resources;
});
});
https://github.com/marcelowa/promise-all-properties
唯一的是,您将需要为对象中的每个promise分配一个属性名称...这是自述文件中的一个示例
https://www.npmjs.com/package/promise-all-properties
这是一个简单的ES2015函数,它接受具有可能是promise的属性的对象,并返回具有已解析属性的该对象的promise。
import promiseAllProperties from 'promise-all-properties';
const promisesObject = {
someProperty: Promise.resolve('resolve value'),
anotherProperty: Promise.resolve('another resolved value'),
};
const promise = promiseAllProperties(promisesObject);
promise.then((resolvedObject) => {
console.log(resolvedObject);
// {
// someProperty: 'resolve value',
// anotherProperty: 'another resolved value'
// }
});
用法:
function promisedProperties(object) {
let promisedProperties = [];
const objectKeys = Object.keys(object);
objectKeys.forEach((key) => promisedProperties.push(object[key]));
return Promise.all(promisedProperties)
.then((resolvedValues) => {
return resolvedValues.reduce((resolvedObject, property, index) => {
resolvedObject[objectKeys[index]] = property;
return resolvedObject;
}, object);
});
}
请注意,@ Bergi的答案将返回一个新对象,而不是对原始对象进行突变。如果您确实要一个新对象,只需将传递给reduce函数的初始化值更改为promisedProperties({a:1, b:Promise.resolve(2)}).then(r => console.log(r))
//logs Object {a: 1, b: 2}
class User {
constructor() {
this.name = 'James Holden';
this.ship = Promise.resolve('Rocinante');
}
}
promisedProperties(new User).then(r => console.log(r))
//logs User {name: "James Holden", ship: "Rocinante"}
使用异步/等待和破折号:
{}
基于这里公认的答案,我想我会提供一种略有不同的方法,该方法似乎更容易遵循:
// If resources are filenames
const loadedResources = _.zipObject(_.keys(resources), await Promise.all(_.map(resources, filename => {
return promiseFs.readFile(BASE_DIR + '/' + filename);
})))
// If resources are promises
const loadedResources = _.zipObject(_.keys(resources), await Promise.all(_.values(resources)));
用法:
// Promise.all() for objects
Object.defineProperty(Promise, 'allKeys', {
configurable: true,
writable: true,
value: async function allKeys(object) {
const resolved = {}
const promises = Object
.entries(object)
.map(async ([key, promise]) =>
resolved[key] = await promise
)
await Promise.all(promises)
return resolved
}
})
// usage
Promise.allKeys({
a: Promise.resolve(1),
b: 2,
c: Promise.resolve({})
}).then(results => {
console.log(results)
})
Promise.allKeys({
bad: Promise.reject('bad error'),
good: 'good result'
}).then(results => {
console.log('never invoked')
}).catch(error => {
console.log(error)
})
[在查找此答案后,我抬头看try {
const obj = await Promise.allKeys({
users: models.User.find({ rep: { $gt: 100 } }).limit(100).exec(),
restrictions: models.Rule.find({ passingRep: true }).exec()
})
console.log(obj.restrictions.length)
} catch (error) {
console.log(error)
}
以查看是否有人已经实现了此功能,显然Promise.allKeys()
确实具有此实现,因此,如果您喜欢此扩展名,请使用它。
编辑:这个问题最近似乎越来越受到关注,所以我想我将为目前在几个项目中使用的这个问题添加当前的解决方案。这比两年前我写的答案底部的代码要好[[lot。
新的loadAll函数假设其输入是一个将资产名称映射到Promise的对象,并且还利用了实验性函数Object.entries,可能并非在所有环境中都可用。
因此,我根据Bergi的答案提出了正确的代码。如果有人遇到相同的问题,就在这里。
// fromEntries :: [[a, b]] -> {a: b} // Does the reverse of Object.entries. const fromEntries = list => { const result = {}; for (let [key, value] of list) { result[key] = value; } return result; }; // addAsset :: (k, Promise a) -> Promise (k, a) const addAsset = ([name, assetPromise]) => assetPromise.then(asset => [name, asset]); // loadAll :: {k: Promise a} -> Promise {k: a} const loadAll = assets => Promise.all(Object.entries(assets).map(addAsset)).then(fromEntries);
// maps an object and returns an array
var mapObjectToArray = function (obj, action) {
var res = [];
for (var key in obj) res.push(action(obj[key], key));
return res;
};
// converts arrays back to objects
var backToObject = function (array) {
var object = {};
for (var i = 0; i < array.length; i ++) {
object[array[i].name] = array[i].val;
}
return object;
};
// the actual load function
var load = function (game) {
return new Promise(function (fulfill, reject) {
var root = game.root || '';
// get resources
var types = {
jpg : getImage,
png : getImage,
bmp : getImage,
mp3 : getAudio,
ogg : getAudio,
wav : getAudio,
json : getJSON
};
// wait for all resources to load
Promise.all(mapObjectToArray(game.resources, function (path, name) {
// get file extension
var extension = path.match(/(?:\.([^.]+))?$/)[1];
// find the getter
var get = types[extension];
// reject if there wasn't one
if (!get) return reject(Error('Unknown resource type "' + extension + '".'));
// get it and convert to 'object-able'
return get(root + path, name).then(function (resource) {
return {val : resource, name : name};
});
// someday I'll be able to do this
// return get(root + path, name).then(resource => ({val : resource, name : name}));
})).then(function (resources) {
// convert resources to object
resources = backToObject(resources);
// attach resources to window
window.resources = resources;
// sequentially load scripts
return game.scripts.reduce(function (queue, path) {
return queue.then(function () {
return getScript(root + path);
});
}, Promise.resolve()).then(function () {
// resources is final value of the whole promise
fulfill(resources);
});
});
});
};
方法中线Promise.obj()
是递归的,并处理较深的对象。
这将创建缺少的process = ...
方法,该方法的作用类似于Promise.obj()
,但适用于对象:
Promise.all()
最好不要使用最后一行!一个更好的主意是将此const asArray = obj => [].concat(...Object.entries(obj));
const process = ([key, val, ...rest], aggregated = {}) =>
rest.length ?
process(rest, {...aggregated, [key]: val}) :
{...aggregated, [key]: val};
const promisedAttributes = obj => Promise.all(asArray(obj)).then(process);
// Promise.obj = promisedAttributes;
导出为可复用的实用程序函数。