2

I am trying to determine a way to "pause" my Promises code until a condition is true, perhaps by using a recursive setTimeout(). For my simple example, I am manually setting waitValue.

I need to wait for a moment, and if waitValue is still false, then merely continue to wait. And of course when waitValue becomes true, then continue processing. Here is what i have pieced together so far:

var counter=0;
function promiseTest() { 
    return new Promise(  (resolve, reject) => {
        if  ( waitValue )  {
            console.log('waitValue is now true');
            resolve('FIRST PROMISE');
        } else  {                      // we need to wait again
            if  ( counter > 1000 ) {   // dont wait forever.
                reject('waited too long');
            } else {
                console.log('WAIT MESSAGE: ' + counter++ );
                setTimeout( promiseTest, 3000);
            }
        }
    })
    .then(   result => {
        return(`SECOND PROMISE:  the result is: ${result}`);
    });
}

And to use it:

promiseTest().then( (result)=>{ console.log('LOCAL MESSAGE: ' + result); });

The following works fine:

var waitValue = true;
promiseTest().then( (result)=>{ console.log('LOCAL MESSAGE: ' + result); });
// waitValue is now true
// LOCAL MESSAGE: SECOND PROMISE:  the result is: FIRST PROMISE

However, the following does not seem to complete as i wanted:

var waitValue = false;
promiseTest().then( (result)=>{ console.log('LOCAL MESSAGE: ' + result); });
// waiting messages appear as expected
waitValue = true;
// waitValue is now true
// no other messages

I have been unable to find a promises example to temporarily execution. An ordinary javaScript example might look like this:

var waitValue = false;
var counter = 0;
(function tester() {
   if  ( waitValue ) {
      console.log('finally true');
   } else {
       if  ( counter > 1000 ) {
           console.log('waited too long');
           process.exit;
       } else {
           console.log('still waiting, counter = ' + counter++);
           setTimeout(tester, 1000);
       }
   }
})();
// wait a few seconds and enter this into the console:
var waitValue = false;

What would a promises script look like to temporarily pause execution? Or maybe Promises should not be used like this at all?

Thank you very much.

edwardsmarkf
  • 1,387
  • 2
  • 16
  • 31
  • 2
    "*maybe Promises should not be used like this at all?*" - yes, you never should need to use polling. Instead, make a promise that is resolved in the place where `waitValue` was set – Bergi Mar 13 '17 at 22:23

2 Answers2

1

The idea is in the right direction. You just need to resolve the current promise also when you have called the function recursively, otherwise your current promise will never fulfil.

Note however, that you create a stack of promises if the wait is long.

function promiseTest(counter = 1000) { 
    return new Promise(  (resolve, reject) => {
        if ( waitValue )  {
            console.log('waitValue is now true');
            resolve('FIRST PROMISE');
        } else if  ( counter <= 0 ) {   // dont wait forever.
            reject('waited too long');
        } else {
            console.log('Count down: ' + counter);
            setTimeout( _ => { // make sure to call `resolve` after the nested promise resolved:
                promiseTest(counter-1).then(resolve);
            }, 3000);
        }
    })
    .then(   result => {
        return `SECOND PROMISE:  the result is: ${result}`;
    });
}

var waitValue = false;

promiseTest().then ( result => {
   console.log('done:', result);
});

// schedule the change of the waitValue:
setTimeout(_ => waitValue = true, 4000);

Note how the output will have some traces of each of the nested chained promises that resolved.

Alternative

I find it more intuitive to perform the recursive call on a function you define within the Promise constructor callback, not on the function that creates the Promise. That way you only create one promise, and you avoid the promise constructor anti-pattern which is present in your idea (and the above working version of it):

function promiseTest(counter = 1000) { 
    return new Promise(  (resolve, reject) => {
        (function loop(counter) {
            if ( waitValue )  {
                console.log('waitValue is now true');
                resolve('FIRST PROMISE');
            } else if  ( counter <= 0 ) {   // dont wait forever.
                reject('waited too long');
            } else {
                console.log('Count down: ' + counter);
                setTimeout( loop.bind(null, counter-1), 3000);
            }
        })(counter); // initial value of count-down
    })
    .then(   result => {
        return `SECOND PROMISE:  the result is: ${result}`;
    });
}

var waitValue = false;

promiseTest().then ( result => {
   console.log('done:', result);
});

// schedule the change of the waitValue:
setTimeout(_ => waitValue = true, 4000);

Note how the output is slightly different from the first version, which reflects that there is only one new promise involved

NB: it is not essential, but I prefer counting down from some value (1000), and to pass it as an argument to the anonymous function that does the looping.

Community
  • 1
  • 1
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! You are not forwarding the eventual error. – Bergi Mar 13 '17 at 22:21
  • I have avoided the antipattern in the second version, I believe. I will add an explicit mention of the anti pattern though. – trincot Mar 13 '17 at 22:25
  • Yes, the second version where you recurse the `loop` only works fine (however I think it lacks the advantages of promises) – Bergi Mar 13 '17 at 22:27
  • "The idea is in the right direction." - that is first time i ever heard that on SO before. thank you for the compliment. your solution is wonderful, especially "loop.bind". your well-crafted algorithm is going to be used extensively in our deepstream-sql interface project. thank you very much. – edwardsmarkf Mar 14 '17 at 19:19
  • 1
    You're welcome ;-) And ... I know what you mean. The arrogance one regularly finds on SO is sometimes hard to bear. But it is not the only sound :-) Glad my contribution was useful to you. – trincot Mar 14 '17 at 19:24
0

setTimeout( promiseTest, 3000); will not work, this might call the promiseTest function again but never resolve the promise created in the outermost invocation.

Instead of messing around with callbacks that much, promisify the asynchronous primitive that you are using, setTimeout:

function wait(t) {
    return new Promise(resolve => {
        setTimeout(resolve, t);
    });
}

and then use that in your polling function:

function promiseTest(counter = 1000) {
    if (waitValue) {
        console.log('waitValue is now true');
        return Promise.resolve('FIRST PROMISE');
    } else if (counter <= 0) { // dont wait forever.
        return Promise.reject(new Error('waited too long'));
    } else {
        console.log('WAIT MESSAGE: ' + counter );
        return wait(3000).then(() => {
            return promiseTest(counter-1);
        });
    }
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375