英文:
recursive resolution object containing promises
问题
我正在寻找类似于p-props
、p-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 < 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);
<!-- 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]}}}}
英文:
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 '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)));
Output:
{"a":1,"b":[2,2.1],"c":{"d":3,"e":{"f":4,"g":{"h":[5,5.1]}}}}
答案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 => recursiveResolve(input));
}
if (typeof result === 'object') {
return Object.fromEntries(Object.toEntries(result).map([k,v] => [k, recursiveResolve(v)]));
}
return result;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论