quoting MDC:
If there is a possibility that your logic could take longer to execute than the interval time, it is recommended that you recursively call a named function using window.setTimeout. For example, if using setInterval to poll a remote server every 5 seconds, network latency, an unresponsive server, and a host of other issues could prevent the request from completing in its alloted time. As such, you may find yourself with queued up XHR requests that won’t necessarily return in order.
For such cases, a recursive setTimeout pattern is preferred:
(function loop(){
setTimeout(function(){
// logic here
// recurse
loop();
}, 1000);
})();
In the above snippet, a named function loop is declared and is immediately executed. loop is recursively called inside setTimeout after the logic has completed executing. While this pattern does not guarantee execution on a fixed interval, it does guarantee that the previous interval has completed before recursing.
I don’t understand how this fixes the problem. Shouldn’t I call loop() from inside the XHR callback and not from setTimeout?
The original problem is related to
setInterval(): it’s possible for two or more AJAX requests to be sent before the reply to the first one is received (depending on the network latency and the timer delay). If that happens, their respective callbacks are not guaranteed to be called in order.The solution is to delay each request independently with
setTimeout()and only schedule the next request after the current request has returned (i.e. in that request’s callback).So, you’re absolutely right: calling
setTimeout()(throughloop()) from the delayed function itself only reimplements a poorersetInterval(). You indeed have to callloop()from the AJAX callback to obtain the expected behavior.