All Case Studies Design Development Interviews Our Way Project Management

Learn Ember Run Loop, pt. 3: Examples

In the last part, we learnt more about the internals of Ember Run Loop. Even earlier, we met the requirements the Ember Run Loop was built for. There is nothing left for us other than examples!

We've got something special for those of you who are a little impatient about learning Ember Run Loop! Download the complete ebook brought to you by Kuba, a Ruby on Rails developer and Ember.JS fan. Enjoy your read!

In the last part, we learnt more about the internals of Ember Run Loop. Even earlier, we met the requirements the Ember Run Loop was built for. There is nothing left for us other than examples!

Ember Run Loop in practice

Schedulers

  • Ember.run.schedule allows you to schedule a passed function to the exact queue pointed out in the first argument.
  • Ember.run.once allows you to schedule a passed function (cannot be anonymous!) by default to the actions queue and makes sure that this function won’t be executed more than once in the current run loop instance.
  • Ember.run.debounce works the same as jQuery debounce but is run loop compliant. It executes a passed function after a specified time and resets the timer every time it’s called again. This means that the passed function won’t be executed in the current run loop - the minimum time that can pass is one millisecond. However, it will be executed in future and all of its Ember API methods will be properly scheduled. Please note that the passed function cannot be anonymous!
  • Ember.run.throttle allows you to execute a passed function immediately and ensures that during a defined period of time it won’t be executed again in any existing run loops. Again, the passed function cannot be anonymous.

Wrappers

  • Ember.run just wraps the passed function in a new instance of Ember run loop. It freezes execution of the current run loop until all the jobs queued during execution of the passed function are flushed.
  • Ember.run.next wraps the passed function in a new instance of Ember run loop that will be executed after one millisecond (again, the smallest period of time between handling events), i.e., the next possible run loop.
  • Ember.run.bind is a very powerful method that is used to embrace external JS libraries. It takes the passed function and context and returns a function that, when executed, will execute the passed function with a proper context and will be wrapped by a new instance of Ember run loop.

Last but not least, I would like to share with you one private Ember API method that might be useful. Ember.run.sync is a method that explicitly makes the current Ember run loop completely flush the sync queue. This is synchronous execution and in next line of code, the sync queue will be empty, so you can be sure that all the bindings are in the proper places. This method can be useful, but remember that private is private and it could easily be changed in future.

Examples, really!

The next few examples will be based on Ember 1.7 - quite an old version. However, this Ember distribution is not a regular one but modified to log every interesting thing about the run loop - when it starts, what it does and when it ends. This implementation is prepared by @eoinkelly and his full tutorial about the run loop may be found here. I prepared a JSBin with this working noisy run loop, with mouse move events removed from run loop triggers. Take a look if you want to try out some more complex ideas.

Nested run loops

To help you better understand how the run loop is just a regular object, instantiated on demand, imagine how nesting run loops might look like in the following situation:

When the user clicks anywhere on the controller template, we open a new run loop instance and pass an anonymous function which logs that it started executing. Then we open the next run loop and do the same. To understand how this works, it’s helpful to use a noisy run loop and let the run loop tell us what it does. Here you can find out how it works live in JSBin.

It turns out that opening a new run loop inside the other one freezes the parent’s execution. As soon as all the queues in the nested run loop are flushed, execution returns to the upper run loop. Notice that when the stack enters the nested run loop, almost nothing from its parent was executed - all the code was just queued, waiting to be executed. Don’t forget about this - it can cause headaches!

Scheduling algorithm

I have analyzed the algorithm that underlies the Ember run loop. This algorithm, though simple, is an extremely important part of the mechanism. It tries to make sure that rendering (the most expensive action) is executed only once. However, there might be situations where you need to make sure that something happens after rendering. Your actions after rendering may introduce new bindings which should be synced. Let’s look at how this works out in a simple example:

As you can see, in the action handler we schedule an anonymous function into the afterRender queue, which, in turn, schedules back to the sync queue. If you go to this live demo, you can see in the console what actually happens. The run loop, after initially flushing all the queues from the sync to afterRender, goes back to the sync queue. Don’t forget - the Ember run loop is safe to schedule in any queue. You can be sure your job will be done.

Example of using Ember.run.sync()

The following example is tricky. It does not adhere to the data-down-action-up convention and it’s not a good practice for your project. But it’s a great illustration of how sync works.

Let’s imagine we have a component with an input field. You use jQuery to handle a change event on that input. In that handler, properly wrapped in a run loop, you set an internal component value and send the action up to the controller that it’s inside.

The controller handles this action and logs the mentioned component value through a binding called ‘value’.

What happens after each change and what is the log output from the controller? If you take a look at this example, you’ll see that after each key click, the log output lags by one character.

Why? To understand what’s going on we have to look in the Ember codebase, in particular - its implementation of sendAction. It turns out that sendAction is executed synchronously, right after it shows up in the code. This is a little bit confusing. Even though the code which inside is wrapped in Ember queues, your console log is immediate and is always late one run loop iteration because of the still-unsynced binding between component and controller.

How can you bypass this problem? Well, we could send that value as an argument of that action and take it from the args rather than from the bindings. But I promised to show you the sync method. Let’s add sync execution in the controller and see what happens.

As you can see, after syncing the bindings there is no delay between the log and the actual value. Again, please note that this design is not a good solution for your projects. Use sync only when the design itself cannot be changed.

We are still not finished with examples at all. I have still at least three of interesting cases with Ember Run Loop that for sure will eventually happen in your work. In next post you will see debounce, observers and handling Ember Run Loop by yourself. Stay tuned!

Don't miss out - read part 1 and part 2 of Kuba's guide! Also, if you'd like to get more insightful content monthly, sign up for our Ruby Brief newsletter.*

Why it is impossible to hire quality engineers in London and how to deal with it
 
READ ALSO FROM Ember.js
Read also
Need a successful project?
Estimate project or contact us