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()
?