Search code examples
javascriptnode.jsrecursionpromisees6-promise

Recursion with Promises means registered callbacks get called many times


So my thinking was right, when recursing with promises, we end up calling all chained callbacks for however many times we recurse, for example

function p() {
    return new Promise(function (r) {
        process.nextTick(r);
    })
}


function recurse(count) {

    return p().then(function () {
        if (count < 10) {
            console.log('count => ', count);
            return recurse(++count);
        }
    }).then(function(){
        console.log('a');
        return 5;
    });

}


recurse(1).then(function () {
    console.log('done');
});

If you run the above, we get:

count =>  1
count =>  2
count =>  3
count =>  4
count =>  5
count =>  6
count =>  7
count =>  8
count =>  9
a
a
a
a
a
a
a
a
a
a
done

Is there a way to register this callback with console.log('a') just once instead of registering it 10 times?

I don't think I want/need this function to be called 10 times, and would like to find a way to have it called just once.

I am actually just as interested in a similar solution for Observables, specifically RxJS5 Observables.


Solution

  • If the recursion is synchronous, you can simply recurse within the .then's function

    new Promise(res => {
        res(); // dummy initial promise
    }).then(() => {
        function recurse(x) { // recursion inside then
            console.log('x', x);
            if (x < 10) return recurse(++x);
            return x;
        }
        return recurse(1); // begin recursion
    }).then(y => { // this only fires once recursion above is resolved
        console.log('y', y);
        return 5;
    }).then(z => console.log('z', z));
    // x 1
    // ... (synchronous)
    // x 10
    // y 10 (value passed from resolving inner promise)
    // z 5 (value returned from previous then)
    

    Or if our recursive function is asynchronous, we can have it return a promise too, so you end up with a recursion which looks like this

    function doWork() {
        function addOne(x) {
            return new Promise((res, rej) => {
                // async bit here
                res(x + 1);
            });
        }
        function recurse(x) {
            if (x < 10) return addOne(x).then(recurse);
            return x;
        }
        return recurse(1);
    }
    

    And in a promise chain, would look like this

    new Promise(res => {
        res(); // dummy initial promise
    }).then(() => {
        return doWork();
    }).then(y => {
        console.log(y);
    });