Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementation of async unfold function in Javascript

I've sometimes found myself wanting an asychronous version of unfold in Javascript: a function that would take a value, then apply an async function to it to get another value, and repeat on this value until some condition applies, producing an array (/ stream / iterator) of values.

E.g.

const res = await asyncUnfold(x=>Promise.resolve(x+1), x => x<6, 2) 
console.log(res)  // [2,3,4,5]

(real examples would do meaningful async operations: e.g. making a repeated network call based on some seed value, and recording all results.)

Does anyone know of a good implementation of this? I can use an async generator or async while loop to do the work, but I'd rather not have to think about coding that each time I want a similar operation. Perhaps there's a good implementation on NPM already...

(see 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 have async array operations, but neither seem to include unfold).

EDIT: in some cases, and perhaps in general, it might be better to NOT include the initial (seed) value in the returned array: e.g.

const res = await asyncUnfoldExclusive(x=>Promise.resolve(x+1), x => x<6, 2) 
console.log(res)  // [3,4,5]
like image 578
phhu Avatar asked Jun 30 '26 12:06

phhu


2 Answers

You can just take the implementation from the article you linked and slap async/await on it:

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;
}

Or using the three-parameter style from your question,

async function asyncUnfold(step, pred, value) {
  const res = [];
  while (pred(value)) {
    res.push(value);
    value = await step(value);
  }
  return res;
}

or implemented in a recursive style

async function asyncUnfold(step, pred, value) {
  return pred(value)
    ? [value, ...await asyncUnfold(step, pred, await step(value))]
    : [];
}
like image 137
Bergi Avatar answered Jul 03 '26 00:07

Bergi


Here is a functional style solution to support the example you have given:

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]
like image 26
trincot Avatar answered Jul 03 '26 01:07

trincot