This is the last post from the Ember Run Loop series. In this part, we deal with a couple of real-life Ember examples. Also, we have a surprise for you - a free Ember Run Loop ebook! Come in and download your version in PDF or Epub format.
In the last post, we tackled a couple of examples to see how the Ember Run Loop works. We did some nesting, scheduling and syncing the bindings queue. In this post, we will check out the situations that are more common in daily development -
debounce, observers and custom Ember Run Loop handling.
Sometimes you would like to execute some actions after some repeated event stops firing - like scrolling or typing input. The Ember run loop provides us with a very simple and convenient method called #debounce that works the same as jQuery debounce but is compliant with the run loop. It fires the passed function after the passed time period. If the debounce is called before the time passes, the timer is reset.
A fully working example can be found here.
I have a tricky question for you: what is executed first in the Ember run loop - observers or computed properties?
Actually, observers are synchronous and are not queued into the Ember run loop. They are executed right after the variable change and will always be executed earlier than any computed property. Let’s take a look at a simple example with an almost identical observer and computed property:
How many times will you see an observer and computed property logged? Check your answer. What’s happening is that observer is fired twice, once when
firstName is changed and once when
lastName is changed. However, the computed property will be fired only once as both of these changes happen in one instance of run loop and will be queued and evaluated before calculating the computed property.
Let’s make use of what you’ve learned in this post. Suppose you need the observer to execute some action on change, but only once in each run loop. You could do it in a computed property, but that would be inconsistent and against convention. Computed properties are for computing while observers are for reacting to changes. Let’s use the Ember.run.once method:
Check out this live demo to see the results. It’s a perfect solution for using observers in a way compliant with the run loop. Their synchronous nature is both a blessing and a curse. However, with your toolset of run loop methods you can use this to your advantage.
Until now, it seems that we don’t need to worry too much about the run loop. It’s always opened on handling a user event and all our actions are executed within it. However, there are situations when Ember is not handling events, but us. These can be custom event handling (like in our former example with sync) or registration of callbacks for AJAX calls or any other asynchronous callback.
In such cases, we simply wrap all the code to be executed in an Ember.run instance. Which isn’t very difficult. On the other hand, there may be situations where we want to pass our controller or component methods to be passed as callbacks to some external jQuery plugins. If we pass them as they are, first of all they won’t be executed in a run loop and second of all, they won’t have a proper ‘this’ scope (and we want our controller or component to be ‘this’ scope, of course). To do so, we can pass a binded method using Ember.run.bind - it both executes the method inside a run loop and binds ‘this’ scope to the controller. Take a look at this snippet from @iStefo - select2 ember wrapper:
Ok, but what happens if we don’t wrap our code in a run loop?
To rephrase the question - can you schedule jobs to the run loop while it is not running? Take a look at following snippet:
The answer is yes, you can. Ember has a mechanism called autorun that initiates the run loop if you try to schedule anything while it’s not running. The thing is simple but powerful mechanism has its disadvantages too. If we totally skip explicitly starting run loops, all of them will be opened by autorun. However, in a testing environment, all asynchronous helpers (like click, fillIn, visit, etc.) will wait for all run loops to flush before going further (e.g. to assets). If autoruns, in some edge cases, don’t cover all the code being executed, you end up with hard-to-debug-problems that only appear in a testing environment. To prevent this, run loops are switched off in testing mode, which forces us to explicitly start run loops everywhere we have tests written (hopefully throughout the entire app!). Now your experience while working in the development mode is better, isn't it?
Take a look at this live demo which shows what happens in testing mode while handling custom events. Remove the first line
Ember.testing = true; and you will return to the development mode, where everything works fine. This is the same example, but with the handler wrapped in a run loop. It works in all situations, regardless of what mode is active.
How is autorun activated? Nearly every Ember API method is compliant with the run loop and internally schedules its respective job -
set schedules setting and bindings,
transitionTo schedules a transition, etc. The scheduling methods that are used in the private API are the same as the public
Ember.run.schedule method. Reading the implementation we see that schedule checks if any autorun is running by making use of the internal run loop references counter. If not, it starts a new one. Fairly easy. Dive deeper and check out this example.
It was quite a long journey through the Ember run loop. You could probably use a structured, one-piece guide that will keep all this information together. You want it - we've got it! Click the cover picture below to download your Ember Run Loop guide in PDF or Epub formats.