Summary: poll() functions with callbacks are available; I haven't found any using native promises. I've tried to adapt some without success. The problem I haven't solved yet is that when the first instance of the function called by setTimeout ends without any return, the .then() listening for it sees the termination as a false and a reject(). then() terminates and doesn't listen for later returns.
Question: How best to help the .then() function stick around for later returns with resolve() or reject()?
The rest of this post is detail. Read what helps.
Available poll functions: I like (https://stackoverflow.com/users/1249219/om-shankar) Om Shankar's response in Calling a function every 60 seconds. David Walsh's poll() is very similar (at https://davidwalsh.name/essential-javascript-functions). Both use callbacks and work well. I found
poll in javascript
which includes a poll() using bluebird-only promises.
Here's my attempt at implementing with native promises.
/**
* poll - checks repeatedly whether a condition exists. When the condition
* exists, returns a resolved standard promise. When it has checked
* long enough, returns a rejected standard promise.
* @param {function} fn - a caller-supplied synchronous function that
* detects a condition in the environment. Returns true if the
* condition exists; otherwise false.
* @param {number} timeout - maximum number of milliseconds
* the caller wants to check param fn();
* reject() the promise at the expiration of param timeout.
* @param {number} interval - minimum number of milliseconds between
* calls to param fn(); resolve() the promise when param fn() first
* reports true.
* @return {promise} - resolved when param fn() returns true;
* rejected if param timeout expires without param fn() returning true
*/
function poll(fn, timeout, interval) {
let endTime = Number(new Date()) + (timeout || 2000)
interval = interval || 250
return Promise.resolve *2
.then(() => { *3
(function p(fn, endTime, interval) {
if (fn()) { return Promise.resolve("Condition is satisfied.") } *4
else {
if (Number(new Date()) <= endTime) {) *5
window.setTimout(p, interval, fn, endTime, interval) *6
}
else {
return Promise.reject("Past endTime; condition not satisfied")
}
}
}()) *7
}) *8
}
Expected usage:
function waitIsOver() { return (<desired condition exists>) }
poll(waitIsOver, 2000, 250) *1
The way I think this is running (please correct me if I'm wrong): After the call to poll() at *1, we quickly return a pending promise at *2 so that poll() knows to wait. Then, we call that promise's then() function at *3. Function p() starts. If fn() (known outside p() as waitIsOver()) returns true at *4, we're good: We return resolve() and poll() at *1 gets the settled promise it seeks.
Then the bad part: If fn() returns false at *4 and we're inside endTime at *5 (which is likely; the first call is unlikely to occur after endTime), we use setTimeout() at *6 to ask JS to make a note in the stack to instantiate another p() after interval time. After that, the first instance of p() terminates at *7. At *8, then() knows that p() terminated without returning anything and interprets the condition as returning false and reject(); with reject(), the promise is settled and can never change. However, after expiration of interval, a successor instance of p() fires up. Anything it returns is lost; the promise is settled and then() has terminated after sending execution on an unwanted path.
How do I convert an existing callback API to promises? recommends an approach with a Promise constructor, resolve() calling callback(), and reject() calling errback. I tried the technique, but I ran into the same problem of the then() function ending before I want it to. I haven't yet figured out how to make then() as patient in waiting as a callback function.
That sets up the question. Again:
Question: How best to help the .then() function stick around for later returns from resolve() or reject()?