在 Javascript 中实现异步展开功能
Implementation of async unfold function in Javascript
我有时发现自己想要 Javascript 中的 unfold
的异步版本:一个函数会接受一个值,然后对其应用异步函数以获得另一个值,然后重复在这个值上直到某些条件适用,产生一个值数组(/流/迭代器)。
例如
const res = await asyncUnfold(x=>Promise.resolve(x+1), x => x<6, 2)
console.log(res) // [2,3,4,5]
(真实的例子会做有意义的异步操作:例如,根据一些种子值进行重复的网络调用,并记录所有结果。)
有谁知道一个很好的实现方式吗?我可以使用异步生成器或异步 while 循环来完成这项工作,但我宁愿不必在每次需要类似操作时都考虑编码。也许在 NPM 上已经有一个很好的实现...
(参见 https://nabilhassein.github.io/blog/unfold/ and https://en.wikipedia.org/wiki/Anamorphism and https://raganwald.com/2016/11/30/anamorphisms-in-javascript.html for some background on unfold. Ramda has an implementation of unfold, but it doesn't seem to do async operations. P-iterator and Rubico 有异步数组操作,但似乎都不包括展开)。
编辑:在某些情况下,也许在一般情况下,最好不要在返回的数组中包含初始(种子)值:例如
const res = await asyncUnfoldExclusive(x=>Promise.resolve(x+1), x => x<6, 2)
console.log(res) // [3,4,5]
您可以从 the article you linked 中获取实现并在其上添加 async
/await
:
async function asyncUnfold(f, seed) { /*
^^^^^ */
var arr = [];
var state = seed;
var next;
while (next = await f(state)) {
// ^^^^^
state = next[1];
arr.push(next[0]);
}
return arr;
}
或者使用您问题中的 three-parameter 样式,
async function asyncUnfold(step, pred, value) {
const res = [];
while (pred(value)) {
res.push(value);
value = await step(value);
}
return res;
}
或以递归方式实现
async function asyncUnfold(step, pred, value) {
return pred(value)
? [value, ...await asyncUnfold(step, pred, await step(value))]
: [];
}
这里有一个功能风格的解决方案来支持你给出的例子:
const asyncUnfold = (next, predicate, start) =>
!predicate(start) ? Promise.resolve([])
: next(start).then(value => asyncUnfold(next, predicate, value))
.then(arr => [start, ...arr]);
asyncUnfold(x => Promise.resolve(x+1), x => x<6, 2).then(console.log); // [2,3,4,5]
另一种方法,受到 的强烈影响但又略有不同,是这样的:
const asyncUnfold = async (f, init) => f (
init,
async (val, seed) => [await val, ...await asyncUnfold (f, seed)],
async () => []
)
asyncUnfold (
(v, next, done) => v > 10 ? done () : next (Promise .resolve (v), v + 1),
1
) .then (console .log, console .warn)
asyncUnfold
接受一个函数和一个初始种子值。它调用传递当前种子的函数,如果我们要继续(使用新值和新种子)调用的函数,以及当我们完成展开时调用的函数。它 returns 一个值数组的 Promise。
我有时发现自己想要 Javascript 中的 unfold
的异步版本:一个函数会接受一个值,然后对其应用异步函数以获得另一个值,然后重复在这个值上直到某些条件适用,产生一个值数组(/流/迭代器)。
例如
const res = await asyncUnfold(x=>Promise.resolve(x+1), x => x<6, 2)
console.log(res) // [2,3,4,5]
(真实的例子会做有意义的异步操作:例如,根据一些种子值进行重复的网络调用,并记录所有结果。)
有谁知道一个很好的实现方式吗?我可以使用异步生成器或异步 while 循环来完成这项工作,但我宁愿不必在每次需要类似操作时都考虑编码。也许在 NPM 上已经有一个很好的实现...
(参见 https://nabilhassein.github.io/blog/unfold/ and https://en.wikipedia.org/wiki/Anamorphism and https://raganwald.com/2016/11/30/anamorphisms-in-javascript.html for some background on unfold. Ramda has an implementation of unfold, but it doesn't seem to do async operations. P-iterator and Rubico 有异步数组操作,但似乎都不包括展开)。
编辑:在某些情况下,也许在一般情况下,最好不要在返回的数组中包含初始(种子)值:例如
const res = await asyncUnfoldExclusive(x=>Promise.resolve(x+1), x => x<6, 2)
console.log(res) // [3,4,5]
您可以从 the article you linked 中获取实现并在其上添加 async
/await
:
async function asyncUnfold(f, seed) { /*
^^^^^ */
var arr = [];
var state = seed;
var next;
while (next = await f(state)) {
// ^^^^^
state = next[1];
arr.push(next[0]);
}
return arr;
}
或者使用您问题中的 three-parameter 样式,
async function asyncUnfold(step, pred, value) {
const res = [];
while (pred(value)) {
res.push(value);
value = await step(value);
}
return res;
}
或以递归方式实现
async function asyncUnfold(step, pred, value) {
return pred(value)
? [value, ...await asyncUnfold(step, pred, await step(value))]
: [];
}
这里有一个功能风格的解决方案来支持你给出的例子:
const asyncUnfold = (next, predicate, start) =>
!predicate(start) ? Promise.resolve([])
: next(start).then(value => asyncUnfold(next, predicate, value))
.then(arr => [start, ...arr]);
asyncUnfold(x => Promise.resolve(x+1), x => x<6, 2).then(console.log); // [2,3,4,5]
另一种方法,受到
const asyncUnfold = async (f, init) => f (
init,
async (val, seed) => [await val, ...await asyncUnfold (f, seed)],
async () => []
)
asyncUnfold (
(v, next, done) => v > 10 ? done () : next (Promise .resolve (v), v + 1),
1
) .then (console .log, console .warn)
asyncUnfold
接受一个函数和一个初始种子值。它调用传递当前种子的函数,如果我们要继续(使用新值和新种子)调用的函数,以及当我们完成展开时调用的函数。它 returns 一个值数组的 Promise。