将配置对象与Promise.all()[复制]

问题描述 投票:2回答:1

我正在使用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] = ...

javascript es6-promise destructuring
1个回答
0
投票

这应该做:


0
投票
我已经编写了可在对象中递归地等待promise并将返回的构造对象返回给您的函数。

Object.fromEntries

10
投票

首先:废弃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

6
投票

我实际上为此创建了一个库,并将其发布到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

3
投票

这是一个简单的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"}


3
投票

使用异步/等待和破折号:

{}

2
投票

基于这里公认的答案,我想我会提供一种略有不同的方法,该方法似乎更容易遵循:

// 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()确实具有此实现,因此,如果您喜欢此扩展名,请使用它。


2
投票

编辑:这个问题最近似乎越来越受到关注,所以我想我将为目前在几个项目中使用的这个问题添加当前的解决方案。这比两年前我写的答案底部的代码要好[[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);


1
投票
缺少// 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); }); }); }); }; 方法

使用香草JavaScript的较短解决方案,没有库,没有循环,没有突变

这里是使用现代JavaScript语法的解决方案,比其他答案更短。

中线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;
导出为可复用的实用程序函数。

1
投票
这里是@Matt的答案,有某些类型和某些重命名,并使用promisedAttributes ECMA-2019

Object.fromEntries

© www.soinside.com 2019 - 2024. All rights reserved.