Understanding the Ember run loop

2 minute read

All of the Ember.js internal code, and most of the stuff you write while creating an Ember.js app runs with the help of the run loop. It is used to batch, order and reorder jobs in a way that is optimised for efficiency. I didn’t notice it’s existence before I ran into problems with the JavaScript setTimeout function callbacks and when manually running async calls to the API.

How Ember run loop works

If you’ve read any of the productivity books out there, they all state that batching similar jobs is the key to success. Ember run loop works in a similar way. By running same or similar jobs at the same time, the browser doesn’t have to skip around from task to task, which eases the burden on it, and if you change the same property a couple of times in the same loop cycle, it will only have to process the last change.

Jobs or tasks are scheduled into different queues, and the queues are processed by priority until they are empty.

Queues

Ember run loop consists of six queues

Ember.run.queues
// => [sync, actions, routerTransitions, render, afterRender, destroy]

Each one of these queues is responsible for some jobs batch that runs inside of it.

  • sync contains the binding synchronisation
  • actions is the general work queue, and the one that generally handles promises.
  • routerTranisitions contains transition jobs in the router
  • render contains the jobs that are associated with rendering the page and updating the DOM
  • afterRender runs after the DOM is complete, after all the render jobs have been executed. This is the perfect place if you want to update the DOM after it has been rendered.
  • destroy is responsible for destroying everything other objects or jobs have scheduled to destroy.

Testing

Ember run loop is really important when running tests in Ember. Ember raises errors in testing mode when we try to schedule work without the run loop.

When should you use the run loop

Strictly speaking, all non Ember api code should be wrapped inside an Ember run loop. That includes:

  • AJAX callbacks
  • DOM updates and event callbacks
  • Websocket callbacks
  • setTimeout and setInterval callbacks
  • postMessage and messageChannel event handlers

Be sure to wrap the callback body in the ember run loop and not the whole invocation. For example:

var controller = this;
socket.on('channel', function (data) {
  Ember.run(controller, function() {
    console.log(data);
    this.set('message', data.message);
  });
});

This fires an Ember run loop after the callback is executed, and the callback code will be executed at exactly the right moment.

Comments