相当于 ES6 Promises 的 BlueBird Promise.props



我想等待一个单词地图来完成承诺。BlueBird 有 Promise.props 来实现这一点,但是在常规 JavaScript 中是否有一种干净的方法来做到这一点?我想我可以制作一个同时包含单词和承诺的新对象,获取这些对象的一系列承诺,然后调用 Promise.all 并将它们放入地图中,但这似乎有点矫枉过正。

适用于普通对象的 Bluebird.props 的实现:

/**
 * This function maps `{a: somePromise}` to a promise that
 * resolves with `{a: resolvedValue}`.
 * @param {object} obj
 * @returns {Promise<object>}
 */
function makePromiseFromObject(obj) {
  const keys = Object.keys(obj);
  const values = Object.values(obj);
  return Promise.all(values)
    .then(resolved => {
      const res = {};
      for (let i = 0; i < keys.length; i += 1) {
        res[keys[i]] = resolved[i];
      }
      return res;
    })
}

我有两个使用 ES6 异步函数的不同实现:

async function PromiseAllProps(object) {
    const values = await Promise.all(Object.values(object));
    Object.keys(object).forEach((key, i) => object[key] = values[i]);
    return object;
}

一行更短,但优化程度较低:

async function PromiseAllProps(object) {
    const values = await Promise.all(Object.values(object));
    return Object.fromEntries(Object.keys(object).map((prop, i) => ([prop, values[i]])));
}

const info = await PromiseAllProps({
    name: fetch('/name'),
    phone: fetch('/phone'),
    text: fetch('/foo'),
});
console.log(info);
{
    name: "John Appleseed",
    phone: "5551234",
    text: "Hello World"
}

如果您正在处理的值是承诺(或承诺和非承诺的混合(的映射 - 并且您希望最终解析的值是解析了所有值的Map

const mapPromise = map => 
    Promise.all(Array.from(map.entries()).map(([key, value]) => Promise.resolve(value).then(value => ({key, value}))))
    .then(results => {
        const ret = new Map();
        results.forEach(({key, value}) => ret.set(key, value));
        return ret;
    });

虽然,我敢打赌有人有一种更光滑的方式来做到这一点,一些新的ES2015 +东西对我来说仍然是新的:p

古老的

异步.js库有一个有前途的对应物:async-q

async-q库支持async库中的所有功能。具体来说async.parallel().乍一看,async.parallel()在接受函数数组(注意一个区别,一个函数数组,而不是 promise(并并行运行它们时看起来就像Promise.all()一样。async.parallel的特别之处在于它还接受一个对象:

const asyncq = require('async-q');
async function foo () {
    const results = await asyncq.parallel({
        something: asyncFunction,
        somethingElse: () => anotherAsyncFunction('some argument')
    });
    console.log(results.something);
    console.log(results.somethingElse);
}

结合 ES6+ Object.entries(( 和 Object.fromEntries(( 的替代实现:

async function pprops(input) {
    return Object.fromEntries(
        await Promise.all(
            Object.entries(input)
                .map(
                    ([k, p])=>p.then(v=>[k, v])
                )
        )
    );
};

建议为此使用像 bluebird 这样的库。如果你真的想自己做这件事,主要的想法是:

  • 解析每个映射值,并使用相应的键将承诺的值连接回
  • 把这些承诺传递给Promise.all
  • 将最终承诺的数组转换回 Map

我会利用 Array.from 的第二个参数,以及可以将键/值对数组传递给 Map 构造函数的事实:

Promise.allMap = function(map) {
    return Promise.all( Array.from(map, 
        ([key, promise]) => Promise.resolve(promise).then(value => [key, value])
    ) ).then( results => new Map(results));
}
// Example data
const map = new Map([
    ["Planet", Promise.resolve("Earth")],
    ["Star", Promise.resolve("Sun")],
    ["Galaxy", Promise.resolve("Milky Way")],
    ["Galaxy Group", Promise.resolve("Local Group")]
]);
// Resolve map values
Promise.allMap(map).then( result => console.log([...result]) );
.as-console-wrapper { max-height: 100% !important; top: 0; }

你可以简单地使用 Promise.all + reduce 来编写它

const promiseProps = (props) => Promise.all(Object.values(props)).then(
      (values) => Object.keys(props).reduce((acc, prop, index) => {
         acc[prop] = values[index];
         return acc;
      }, {})
    );

为了普通 js 对象的完整性,还有一个不错的 lodash 变体

async function makePromiseFromObject(obj: {[key: string]: Promise<any>}) {
    return _.zipObject(Object.keys(obj), await Promise.all(Object.values(obj)))
}    

最新更新