Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-recursive method to iterate over Promise iterator

I've developed a client library that exposes a method called iterator(). This method returns a Promise instance created using require('promise') library, which is completed with an iterator object.

This object contains a method called next() which returns a Promise which is completed with a complex object like this: {done: [true|false], key: _, value: _}

Although iterator() might pre-fetch some elements, next() needs to return a Promise in case it results in a remote call.

Now, say a user wants to iterate over all elements until the Promise returned by next() returns an object containing done: true.

I've managed to achieve this using the following recursive method (I originally found this solution in this answer):

var iterate = client.iterator();

iterateTeams.then(function(it) {

  function loop(promise, fn) {
    // Simple recursive loop over iterator's next() call
    return promise.then(fn).then(function (entry) {
      return !entry.done ? loop(it.next(), fn) : entry;
    });
  }

  return loop(it.next(), function (entry) {
    console.log('entry is: ' + entry);
    return entry;
  });

});

The question is, would it be possible, using require('promise') library, to build a non-recursive solution? The reason I'm interested in a non-recursive method is to avoid blowing up if the number of entries to iterate over is too big.

Cheers, Galder

like image 897
Galder Zamarreño Avatar asked Apr 01 '16 16:04

Galder Zamarreño


2 Answers

The reason I'm interested in a non-recursive method is to avoid blowing up if the number of entries to iterate over is too big

Don't fear. Asynchronous "recursion" (sometimes dubbed pseudo-recursion) does not grow the call stack, it's much like tail recursion. You won't ever get a stackoverflow exception.

And if the promise library is implemented reasonably, this should not even grow the memory - see Building a promise chain recursively in javascript - memory considerations for details.

like image 56
Bergi Avatar answered Nov 12 '22 00:11

Bergi


Without new syntax or a library - generally no.

Well, if you're using babel, you can use ES2018 (:P) async iteration:

for await (const team of iterateTeams) {
   // do something with team
}

read more about it here

Otherwise, you can use generators with ES2016 async/await syntax:

for(var it = iterateTeams(); !done; ({done, value}) = await it.next()) {
    // work with value
}     

Or with available today ES2015 generator syntax and a pump through bluebird:

// inside a Promise.corutine  
for(var it = iterateTeams(); !done; ({done, value}) = yield it.next()) {
   // work with value
}     
like image 1
Benjamin Gruenbaum Avatar answered Nov 11 '22 23:11

Benjamin Gruenbaum