递归解析包含承诺的对象

huangapple go评论75阅读模式
英文:

recursive resolution object containing promises

问题

我正在寻找类似于p-propsp-all npm库的功能,但可以递归解决Promise。

const values = {
    a: () => Promise.resolve(1),
    b: [() => Promise.resolve(2)],
    c: {
        d: () => Promise.resolve(3),
    },
};
console.log(await resolve(values, { concurrency: 2 }));

我期望的结果是{a: 1, b: [2], c: {d: 3}},并且同时只运行2个Promise。

我看到可以通过组合p-all、p-props/p-map以及它们的映射器来实现这个目标。但是不确定并发性方面是否存在现有的方法。

英文:

I'm looking at a functionality similar to p-props, p-all npm library but which recursively resolves promises

const values = {
    a: () => Promise.resolve(1),
    b: [() => Promise.resolve(2)],
    c: {
        d: () => Promise.resolve(3),
    },
};
console.log(await resolve(values, { concurrency: 2}));

What I'm expecting is {a: 1, b: [2], c: {d: 3}} and only 2 promises should be running at a time

I see that this can be accomplished by clubbing together p-all, p-props/p-map and their mappers. But not sure about concurrency. Is there any existing approach to such a requirement?

答案1

得分: 2

你可以跟踪待处理的承诺(使用 Set),并使用 Promise.race 来检测其中一个何时解决。然后,您可以执行下一个任务并再次将其添加到该集合中,...等等。

使用递归来深入了解这些任务,并且当承诺解决时(使用 Promise.all),使用已解决的值重建对象结构。

一个演示,其中承诺需要不同的时间来解决:

function resolveNested(tasks, {concurrency = 2} = {}) {
    const pendingPromises = new Set;

    function enqueue(task) {
        if (pendingPromises.size < concurrency) { // We have room to create the promise now
            const promise = task().then(value => {
                pendingPromises.delete(promise);
                return value;
            });
            pendingPromises.add(promise);
            return promise;
        }
        // No room now. Try again when one pending promise has resolved
        return Promise.race([...pendingPromises]).then(() => enqueue(task));
    }

    function dfs(item) {
        if (typeof item === "function") return enqueue(item);
        if (Object(item) !== item) return item; // primitive
        // Apply recursion, and collect resolved values:
        const promise = Promise.all(Object.values(item).map(dfs));
        if (Array.isArray(item)) return promise;
        return promise.then(values => // Rebuild object structure
            Object.fromEntries(Object.keys(item).map((key, i) => [key, values[i]]))
        );
    }

    return dfs(tasks);
}

// Some helper functions for a demo:
const elapsed = () => Math.floor((performance.now() - start) / 100) / 10 + " sec";

const delay = async (ms, val) => {
    console.log(`${elapsed()} - Creating promise: delay(${ms}, ${val})`);
    await new Promise(resolve => setTimeout(resolve, ms));
    console.log(`${elapsed()} - Resolved promise: delay(${ms}, ${val})`);
    return val;
};

// Demo:
const values = {
    a: () => delay(1000, "A"),
    b: [() => delay(2000, "b[0]"), () => delay(500, "b[1]")],
    c: {
        d: () => delay(3000, "c.d"),
        lit: "literal",
        e: () => delay(500, "c.e"),
    },
};

const start = performance.now();
resolveNested(values, { concurrency: 2}).then(console.log);
英文:

You could keep track of the pending promises (in a Set) and use Promise.race to detect when one of those resolves. Then you can execute the next task and add it again to that set, ...etc.

Use recursion to drill down to those tasks, and as promises resolve (using Promise.all), rebuild the object structure with the resolved values.

A demo with promises that take varying times to resolve:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function resolveNested(tasks, {concurrency = 2} = {}) {
const pendingPromises = new Set;
function enqueue(task) {
if (pendingPromises.size &lt; concurrency) { // We have room to create the promise now
const promise = task().then(value =&gt; {
pendingPromises.delete(promise);
return value;
});
pendingPromises.add(promise);
return promise;
}
// No room now. Try again when one pending promise has resolved
return Promise.race([...pendingPromises]).then(() =&gt; enqueue(task));
}
function dfs(item) {
if (typeof item === &quot;function&quot;) return enqueue(item);
if (Object(item) !== item) return item; // primitive
// Apply recursion, and collect resolved values:
const promise = Promise.all(Object.values(item).map(dfs));
if (Array.isArray(item)) return promise;
return promise.then(values =&gt; // Rebuild object structure
Object.fromEntries(Object.keys(item).map((key, i) =&gt; [key, values[i]]))
);
}
return dfs(tasks);
}
// Some helper functions for a demo:
const elapsed = () =&gt; Math.floor((performance.now() - start) / 100) / 10 + &quot; sec&quot;;
const delay = async (ms, val) =&gt; {
console.log(`${elapsed()} - Creating promise: delay(${ms}, ${val})`);
await new Promise(resolve =&gt; setTimeout(resolve, ms));
console.log(`${elapsed()} - Resolved promise: delay(${ms}, ${val})`);
return val;
};
// Demo:
const values = {
a: () =&gt; delay(1000, &quot;A&quot;),
b: [() =&gt; delay(2000, &quot;b[0]&quot;), () =&gt; delay(500, &quot;b[1]&quot;)],
c: {
d: () =&gt; delay(3000, &quot;c.d&quot;),
lit: &quot;literal&quot;,
e: () =&gt; delay( 500, &quot;c.e&quot;),
},
};
const start = performance.now();
resolveNested(values, { concurrency: 2}).then(console.log);

<!-- end snippet -->

答案2

得分: 0

使用 mapper(value, key) 来执行以下操作

接收当前值和键作为参数。如果值是一个 Promise,mapper 将接收这个 Promise 解析的值。期望返回一个 Promise 或值。

import pProps from 'p-props';

const values = {
  a: Promise.resolve(1),
  b: [Promise.resolve(2), Promise.resolve(2.1)],
  c: {
    d: Promise.resolve(3),
    e: {
      f: Promise.resolve(4),
      g: {
        h: [Promise.resolve(5), Promise.resolve(5.1)],
      },
    },
  },
};

const createMapper = (value, key) => {
  const mapper = (v, k) => {
    if (Array.isArray(v)) {
      return pProps(Object.fromEntries(v.map((e, i) => [i, e]))).then(
        Object.values
      );
    }
    if (Object.prototype.toString.call(v) === '[object Object]') {
      return pProps(v, mapper);
    }
    return v;
  };
  return mapper(value, key);
};

pProps(values, createMapper).then((r) => console.log(JSON.stringify(r)));

输出:

{"a":1,"b":[2,2.1],"c":{"d":3,"e":{"f":4,"g":{"h":[5,5.1]}}}}

stackblitz

英文:

Use the mapper(value, key) do this

> Receives the current value and key as parameters. If a value is a Promise, mapper will receive the value this Promise resolves to. Expected to return a Promise or value.

import pProps from &#39;p-props&#39;;

const values = {
  a: Promise.resolve(1),
  b: [Promise.resolve(2), Promise.resolve(2.1)],
  c: {
    d: Promise.resolve(3),
    e: {
      f: Promise.resolve(4),
      g: {
        h: [Promise.resolve(5), Promise.resolve(5.1)],
      },
    },
  },
};

const createMapper = (value, key) =&gt; {
  const mapper = (v, k) =&gt; {
    if (Array.isArray(v)) {
      return pProps(Object.fromEntries(v.map((e, i) =&gt; [i, e]))).then(
        Object.values
      );
    }
    if (Object.prototype.toString.call(v) === &#39;[object Object]&#39;) {
      return pProps(v, mapper);
    }
    return v;
  };
  return mapper(value, key);
};

pProps(values, createMapper).then((r) =&gt; console.log(JSON.stringify(r)));

Output:

{&quot;a&quot;:1,&quot;b&quot;:[2,2.1],&quot;c&quot;:{&quot;d&quot;:3,&quot;e&quot;:{&quot;f&quot;:4,&quot;g&quot;:{&quot;h&quot;:[5,5.1]}}}}

stackblitz

答案3

得分: 0

这可能会起作用:

async function recursiveResolve(input) {

  const result = await input;
  if (Array.isArray(result)) {
    return result.map(item => recursiveResolve(input));
  }
  if (typeof result === 'object') {
    return Object.fromEntries(Object.entries(result).map([k,v] => [k, recursiveResolve(v)]));
  }
  return result;

}
英文:

This might work:

async function recursiveResolve(input) {

  const result = await input;
  if (Array.isArray(result)) {
    return result.map(item =&gt; recursiveResolve(input));
  }
  if (typeof result === &#39;object&#39;) {
    return Object.fromEntries(Object.toEntries(result).map([k,v] =&gt; [k, recursiveResolve(v)]));
  }
  return result;

}

huangapple
  • 本文由 发表于 2023年7月13日 14:18:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76676434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定