Backburner.js and the

Ember Run Loop


Erik Bryn

@ebryn

erikbryn.com

It'll all about coalescing...
What do I mean by coalescing?

What ideally should happen here?



model.set('firstName', 'Erik');
model.set('lastName',  'Bryn');
          

<div>{{fullName}}</div>
          

Backbone.js



// silent suppresses the change events
model.set('firstName', 'Erik', {silent: true});
// triggers model and lastName change events
model.set('lastName',  'Bryn');
          

// triggers model, firstName, and lastName change events all at once
model.set({firstName: 'Erik', lastName: 'Bryn'});
          

Backbone.js


In Backbone, views subscribe to model change
events and typically rerender when they fire.


app.TodoView = Backbone.View.extend({
  // ...

  initialize: function () {
    this.listenTo(this.model, 'change', this.render);
  },

  render: function() {
    // do our DOM manipulations here. they'll get run many times :(
  }

  // ...
});
          

// triggers the view to rerender
model.set('firstName', 'Erik');
// triggers the view to rerender
model.set('lastName',  'Bryn');
          
Naively implemented Backbone apps will tend to
do a lot more DOM manipulations than are necessary.
This is why we want to coalesce.
Coalescing requires deferring of actions.

Enter Backburner.js stage left.
https://github.com/ebryn/backburner.js

Simple Backbone example


app.TodoView = Backbone.View.extend({
  // ...

  initialize: function () {
    this.listenTo(this.model, 'change', this.render);
  },

  render: function() {
    // put the rerender on the backburner!
    backburner.deferOnce('render', this, this.actuallyRender);
  },

  actuallyRender: function() {
    // do our DOM manipulations here. will only be called once.
  }

  // ...
});
          

// ... somewhere in our app code ...
backburner.run(function() {
  model.set('firstName', 'Erik');
  model.set('lastName',  'Bryn');
});

// our view has been rerendered only once, thanks to backburner!
          

How Ember uses Backburner


It's completely abstracted away from you.


Most of the time you don't even have to know it's there.

How Ember uses Backburner


Ember.run = function(target, method) {
  var ret;
  try {
    ret = backburner.run.apply(backburner, arguments);
  } catch (e) {
    if (Ember.onerror) {
      Ember.onerror(e);
    } else {
      throw e;
    }
  }
  return ret;
};

Ember.run.begin = function() {
  backburner.begin();
};

Ember.run.end = function() {
  backburner.end();
};

Ember.run.schedule = function(queue, target, method) {
  backburner.schedule.apply(backburner, arguments);
};

Ember.run.hasScheduledTimers = function() {
  return backburner.hasTimers();
};

Ember.run.cancelTimers = function () {
  backburner.cancelTimers();
};

Ember.run.sync = function() {
  backburner.currentInstance.queues.sync.flush();
};

Ember.run.later = function(target, method) {
  return backburner.later.apply(backburner, arguments);
};

Ember.run.once = function(target, method) {
  var args = slice.call(arguments);
  args.unshift('actions');
  return backburner.scheduleOnce.apply(backburner, args);
};

Ember.run.scheduleOnce = function(queue, target, method) {
  return backburner.scheduleOnce.apply(backburner, arguments);
};

Ember.run.next = function() {
  var args = slice.call(arguments);
  args.push(1);
  return backburner.later.apply(backburner, args);
};

Ember.run.cancel = function(timer) {
  return backburner.cancel(timer);
};
          
So what is this Run Loop I keep hearing about?
I think it's a confusing name.
The Run Loop isn't actually a loop.
It's a set of queues that Ember defers certain types of actions to.
sync, actions, render, afterRender, and destroy
sync is for synchronizing bindings
actions is the main queue that your app's actions are enqueued in

render is where all the DOM-related changes
made for you by Ember are enqueued


afterRender is where you can enqueue DOM manipulations
that need to occur after the render queue has been flushed

destroy is where Ember enqueues object destruction
Debugging deferred / asynchronous actions is hard.
But don't worry... Backburner has your... back?
Q&A
Thanks!

Erik Bryn

@ebryn

erik@erikbryn.com