Search code examples
javascriptecmascript-6promisebluebirdq

Go through undetermined number of Promises sequentially


I have this class that has method next returning a Promise.

class PromiseGenerator {
    constructor() {
        this.limit = 100;
        this.counter = 0;
    }
    next() {
        this.counter++;
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(this.counter <= this.limit ? this.counter : false);
            }, 500);
        });
    }
}

Though example shows 100 it could return unlimited number of promises.

I need to execute all the promises in sequential order.

How can I achieve it?

Only solution I came up so far is recursive:

const source = new PromiseGenerator();

(function loop() {
    source.next().then(counter => {
        if (counter) {
            console.log(counter);
            loop();
        } else {
            console.log('Done');
        }
    });
})();

As I understand Node at the moment does not optimize tail calls and it may lead to stack growth.

Is there a better way of doing this?

if some Promise library has it it will work but it would be nice to understand how to achieve it without library as well.

Update 1: Sorry I didn't make it clear right away: I am not in control of PromiseGenerator class, it is something I can use but cannot change. So the question is how to handle this situation.

Update 2: I went with @eikooc solution: Without generators but just with async/await. See example below.

Thanks!


Solution

  • Generators are a perfect match for this. Construct a generator with the function* keyword:

    function* promiseGenerator() {
      while(!someCondition) {
        yield new Promise((resolve, reject) => {})
      }
    }
    

    And then call it with:

    const source = promiseGenerator()
    source.next()
    

    This will continue to give you new values. The return looks like this {value: Promise, done: false} until it is finished. When the generator finishes the done value will change to true

    If you want to keep using the class and just want a loop. You can also combine your class with a async function:

    async function loop() {
      const source = new PromiseGenerator()
      while (true) {
        const result = await source.next()
        if (result) {
          console.log(result)
        } else {
          console.log('done')
          break
        }
      }
    }
    
    loop()