7

I have a promise chain with a recursive promise doAsyncRecursive() in the middle like so:

doAsync().then(function() {
    return doAsyncRecursive();
}).then(function() {
    return doSomethingElseAsync();
}).then(function(result) {
    console.log(result);
}).catch(errorHandler);

doAsyncRecursive() has to do something and if it at first does not succeed, i then after want to try every 5 seconds until it does. This is what my promise function looks like:

function doAsyncRecursive() {
    return new Promise(function(resolve, reject) {
        //do async thing
        if (success) {
            resolve();
        } else {
            return new Promise(function(resolve, reject) {
                setTimeout(function() {
                    doAsyncRecursive();
                }, 5000);
            });
        }
    });
}

But when I execute, the chain does not continue after doAsyncRecursive() is successful on the 2nd try and resolve() is called (it continues if the attempt is successful on the 1st try however).

What pattern do I need to make this work?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Syd Reid
  • 73
  • 1
  • 4

1 Answers1

7

Catch the failure, wait five seconds, then try again.

function doAsyncRecursive() {
    return doAsyncThing().catch(function() {
        return Promise.delay(5000).then(doAsyncRecursive);
    });
}

Here doAsyncThing is a function corresponding to the //do async thing comment in the OP's code, defined as returning a promise. In the original code, the success or failure of the "do async thing" is tested using a success flag, but by definition asynchronous routines do not deliver such a flag; they deliver their results either via a callback or a promise. The code above assumes that doAsyncThing returns a promise. It also assumes that "failure", in the sense of "does not return the response i want", is represented by that promise rejecting. If instead "success" or "failure" is to be defined as some particular value of a fulfilled promise, then you'd want to do

function doAsyncRecursive() {
    return doAsyncThing().then(function(success) {
        if (success) return success;
        else return Promise.delay(5000).then(doAsyncRecursive);
    });
}
  • sorry but i am having trouble understanding this solution as I dont know what you mean by the async() call. maybe i wasnt clear - doAsync() and doAsyncRecursive() are completely different functions doing different asynchronous tasks, and doAsyncRecursive() is just trying to call doAsyncRecursive() over and over again when it does not return the response i want. was this your understanding as well? – Syd Reid Jun 22 '15 at 04:31
  • thanks for the update. i modified my code to reject my promise based on a test condition and made a similar wrapper function that returns my async promise like you suggested, and it works now! i see now how everything falls through to return a promise so the chain can continue. cheers :) – Syd Reid Jun 22 '15 at 10:14
  • This solution leaks memory on every `catch()`. – kikap Jun 29 '16 at 05:37
  • @kikap Kindly elaborate. –  Jun 29 '16 at 09:03
  • @torazaburo I've found your answer when trying to figure out why my memory usage slowly grows with each request. I'm using a very similar method to restart a blocking-promise-returning function, and I know that according to the Promise spec the recursive call made from `.then` handler doesn't grow the stack. But in my implementation (measured with `process.memoryUsage()` with `--expose-gc` parameter to `node` and `gc();` calls in the program) I observe a slow constant growth of used memory. I'm using bluebird. – kikap Jun 30 '16 at 09:26
  • @kikap That's interesting. Is there any chance you could try with native promises or another promises implementation? Is this something you maybe should report as a bug against Bluebird? –  Jun 30 '16 at 13:30