The Ember run loop is an extremely important part of Embers internals. The run loop is used to batch, order and work in a way that's most efficient. Before we can understand why we should care, we need to understand a little more about the Ember run loop itself first.


Queues

Ember uses six queues to process work. Each queue is processed in order to completion in priority order. While being a little old you can see an example of this here. Ember batches things together in order to minimize the amount of work needed to get everything done. That's one reason Ember runs quick.

Here are the queues and a general description of what each queue does.

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

  • sync - binding syncronization jobs
  • actions - general work and promises
  • routerTransitions - transition jobs in the router
  • render - jobs meant for rendering, usually for updating DOM
  • afterRender - This is ran after all previously scheduled render tasks are completed. This queue is typically used for 3rd party applications.
  • destroy - Tear down of objects

Is This Executed At All Times?

No. Essentially the loop will run in response to certain user and timer events. This is not the same as a while(true) loop on a server otherwise control would never be given back and the application would hang.

Ember.Run Namespace

The good thing is we can manipulate the run loop if needed. Three common methods of doing this are Ember.run.schedule, Ember.run.later and Ember.run.next. Ember.run.next will run in a separate run loop after the current run loop has finished and control has been returned to the system. The Ember.run.schedule will run in the current run loop to the named queue. Ember.run.later we will discuss a little later.

When Do You Need To Use This?

The answer is not very often. Only certain types of situations will lend itself to using Ember.run. Most of the time it will involve one of the following:

  • asynchronous callbacks
  • websockets
  • DOM updates
  • setTimeout and setInterval callbacks
  • PostMessage and messsageChannel event handlers

Keep in mind one of the points of Ember.run is to make sure all your asynchronous calls are tracked by the run loop and that they are executed properly. I've seen a lot when testing as well. Use it wisely.

Ember.run.schedule

One good reason you should use Ember.run.schedule is when you're dealing with plugins that are manipulating the DOM. For example you might need to schedule the afterRender queue inside didInsertElement when using jQuery plugins.

Without scheduling the runloop you might run into issues where the DOM never gets updated since the template might not have fully rendered when the manipulation was occuring. By scheduling it in the afterRender queue you can guarantee the rendering has been completed.

What About Ember.run.later?

As mentioned earlier you may need to worry about Ember's run loop when you're dealing with timers. Ember.run.later is a great solution when dealing with them. It should be used whenever you need to run some action after a period of time instead of using setTimeout(). It's often more efficient as well. During integration testing you should use it instead of setTimeout().

Update As pointed out by flylib in the comments Ember.run.later may not always be the best idea if you want to run integration tests. He recommends using setTimeout with Ember.run.bind. Just beware your mileage may vary.

Let's look at an example. Let's say you wanted to show the current time listed in your application up to the second. Then you wanted to see the difference between the time the route was loaded and the current time. You could do something like this below.

Ember.run.later Demo

Here we are using the momentjs library. When the application route is loaded we include the setupController hook which we then tell to run the startWatchingTime function.

// Application Route 
...
  startWatchingTime: function(controller){
    var self = this;
    controller.set('currentTime', moment());
    Em.run.later(function(){
      self.startWatchingTime(controller);
    }, 1000);

We set the 'currentTime' using the momentjs library. The Em.run.later will wait and fire after a second has passed. It's recursive and will call its self again. We could have set at some point an exit condition, however for the purposes of this demo it's not needed.

Later in this example we set another moment value in the index route. This time we don't call the Em.run.later.

//Index route
...
  setupController: function(controller, model){
   this._super(controller, model);

    controller.set('anotherTime', moment());

  }

Now we have our two times and we can use our handlebars helper to find the difference.

//index template
...
   Time Diff {{time-diff  controllers.application.currentTime anotherTime 'seconds'}}<br><br>
    Current Time: {{controllers.application.currentTime}}<br>
    Startup Time: {{anotherTime}}

If we wanted to we could use Ember.cancel to stop the timer. This will work with run.later(), run.once(), run.next(), run.debounce() and run.throttle().

Why You Should Care

We should care for a few reasons. Most importantly it's always good to look inside and dig a little deeper to figure out how things work. Learning the run loop gives you a little deeper understanding on what's happening. It will come in handy in the future.

As your applications grows, you will run into issues with complicated asynchronous calls and unusual DOM updates. At that point manipulating the Ember run loop might be the only way to continue on.

More Resources

There is a lot more to learn. I highly suggest looking at the following articles.

Future

I admit I'm not an expert in the Ember run loop, although feel free to chime in the comments if you have any questions. I'll try to help.

Image credit to Callpromise