Asynchronous Loops

JavaScript's built in looping mechanisms, 'while', 'for', 'array.forEach', etc, are all synchronous. They work well for processing a set of things in an object or array that are in memory already. There are many other tasks that can be modeled well as an asynchronous loop. For example render loops, polling, and cron like tasks. Letting these loops terminate too early or be unexpectedly infinite are some of the JavaScript common pitfalls.

Whatever your use case, the ideas behind an asynchronous loop follow a common pattern. Check your condition, do some work, set yourself to be called again in the future.

Infinite loops don't need to check their condition. They are like `while (true)`, but more useful because they don't block the event loop so everything else keeps working. A render loop is a good example of this.

var count = 0 var render = function () { document.body.innerHTML = count++ window.requestAnimationFrame(render) } window.requestAnimationFrame(render)

Now we have a timer that does it's best to display a number and increment itself 60 times a second.

This doesn't look like a loop, because many parts of the mechanism aren't in our control. We skip the condition check entirely because we want to keep going forever. Queueing up the next iteration is handled by calling requestAnimationFrame, and it handles the frequency at which our code is called.

Another common thing you want to do is poll. A lot of time polling is the easy road out of something that really should be done in another way, in reaction to events or streams, on the fulfillment of a promise, etc...

Avoid polling in those cases, polling makes things more complicated, and less reliable. It has performance overhead. It hogs cpu and drains batteries. But sometimes it's required. And sometimes it's the right thing to do even if it isn't required, because the other way would be hard, and you don't have the time to do it right. But make that tradeoff consciously.

Here is a contrived example of what polling for the results of an http request could look like. It is a prefect example of a time when polling is wholly unnecessary.

var request = require('request') var content = '' var useContent = function () { if (content) { console.log(content) } else { setTimeout(useContent, 10) } } useContent() request('http://nrn.io/javascript-common-pitfalls.html', function (err, res, body) { content = body } )

So far we've been using timers for our loops, and the frequencies are entirely arbitrary. We can do this with any kind of asynchronous operation though, here's a simple way to aggressively poll for changes in a wiki page using node style callbacks.

var request = require('request') var start = Date.now() var url = 'http://wiki.nrn.io/welcome-visitors.json' request(url, function poll (err, res, body) { var journal = JSON.parse(body).journal if (journal[journal.length-1].date > start) { console.log('New Version!') return } //if nothing has changed, ask again request(url, poll) } )

Naming the function expression 'poll' lets us easily pass a reference to it to the next request. This pattern of having the callback to an asynchronous function set itself to be called again in the future works well with most kinds of async functions.