在 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。