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.
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.
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);
});
}
}