0

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 2
    Return a promise in your first `.then`, Recurse in that promise. resolve that promise when the recursion is complete. – Paul S. Jan 01 '17 at 04:18
  • @Paul is the answer I just posted what you are talking about? – Alexander Mills Jan 01 '17 at 04:19
  • _"So my thinking was right, when recursing with promises"_ Note, the pattern at Question is not considered to be "recursion" proper. See [What are the boundaries of recursion?](http://stackoverflow.com/questions/40499044/what-are-the-boundaries-of-recursion), [In JavaScript, what are the differences between “recursion”, “a non-terminating procedure that happens to refer to itself”, and “repeated scheduling”?](http://stackoverflow.com/questions/41292992/in-javascript-what-are-the-differences-between-recursion-a-non-terminating). – guest271314 Jan 01 '17 at 04:58
  • 1
    The answer you seek is already manifest in the question. You know how to recurse and how to chain promises. After that, it's just a matter either including or excluding `console.log(...)` at each stage. If any knowledge is lacking, it's how to deliver a value down the promise chain, but that's not the question asked. – Roamer-1888 Jan 01 '17 at 08:10
  • @Roamer-1888 sometimes there are clever / counterintutive solutions to simple questions and I often crowdsource to arrive at such things – Alexander Mills Jan 01 '17 at 09:20
  • Absolutely, but in the absence of some counterintuitive approach, it should also be of value to learn that you already know all there is to know (in the context of the question). – Roamer-1888 Jan 01 '17 at 10:20
  • I don't think what you're doing/seeing is wrong. If I recurse 10 times into a function, I would expect **everything** that's inside to execute 10 times. You should waterfall/chain promises then finish with the final `console.log('a');` thing. `Observable.from().subscribe()` could also do what you wish – Sebas Jan 01 '17 at 16:14

2 Answers2

2

I guess the only solution is to nest the remainder of your code inside the first promise callback like so:

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


function recurse(count) {
    return p().then(function () {
        if (count < 10) {
            return recurse(++count);
        } else {
            // all your remaining code would have to go here
            console.log('a');
            return 5; // or return someOtherPromise() or anything
        }
    });
}

recurse(1).then(function () {
    console.log('done');
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • You don't even need that `someOtherPromise` (which you had not in your question either). Just `else return 5;` – Bergi Jan 01 '17 at 22:10
  • @Bergi yeah it was just to demonstrate that you can put promises in there too and they will only run after the recursion has completed – Alexander Mills Jan 01 '17 at 22:14
1

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);
});
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • _"Recurse within another promise"_ See [What are the boundaries of recursion?](http://stackoverflow.com/questions/40499044/what-are-the-boundaries-of-recursion), [In JavaScript, what are the differences between “recursion”, “a non-terminating procedure that happens to refer to itself”, and “repeated scheduling”?](http://stackoverflow.com/questions/41292992/in-javascript-what-are-the-differences-between-recursion-a-non-terminating) – guest271314 Jan 01 '17 at 05:03
  • @Paul, so basically don't use promises for the recursion bit :) – Alexander Mills Jan 01 '17 at 05:31
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! And no, when you're doing the recursion in the asynchronous part then [you don't need to consider the stack size](http://stackoverflow.com/q/29925948/1048572). – Bergi Jan 01 '17 at 22:06
  • @Bergi thanks for pointing out this anti-pattern. I've edited my answer and would like your input to confirm I've understood properly – Paul S. Jan 02 '17 at 00:23
  • 1
    Thanks, looks good to me now. The only problem is with unnecessarily using promises at all for the synchronous recursion in the first snippet, you could simplify that to `return x` and `return recurse(1);` without any `new Promise` in the `then` callback – Bergi Jan 02 '17 at 11:45