Understanding the run loop

The last topic of the runtime environment is the run loop. The run loop is an extremely important adaptation that makes SproutCore outperform other frameworks and makes it possible to achieve incredible performance in a web app in spite of the many constraints of the browser. The reason I haven't talked much about the run loop before this point is because as a developer you generally don't need to think about the run loop and I don't want anyone to get into the habit of trying to manipulate the run loop timing without first fully understanding what they're doing.

For most projects, you should be able to ignore the run loop entirely simply by always using SproutCore technologies that are already run loop aware. This includes using SC.Request for XHR, using SC.Timer for timers and using SC.Event for custom event handling for example. But in case that you do need to create a new run loop aware event handler or adjust the execution timing of some code, I will give you a brief overview of the function of the run loop.

In SproutCore there is actually only a single run loop, which is idle until a "run" is triggered. All of the standard document events like 'mousedown', 'touchstart', 'keydown', and so on, will trigger a run of the run loop, as will the asynchronous event handlers in the previously mentioned SC.Request and SC.Timer classes.

When the run loop runs, it coordinates code execution so that by the end of the run loop all the changes from our asynchronous event have flushed through bindings across the entire application and the display has updated if necessary. This describes two very important features of the run loop. The first is that at the end of the run loop the state of our application is synchronized, which is a deeply important point that I can't do justice to enough in such a short section, but by keeping the "truth" inside the code and not in the DOM, our user interface will always be in sync and we have made a gigantic leap ahead in our ability to deliver complex applications. The second feature is that SproutCore coordinates any display updates to the end of the current run loop. For example, if a property that affects the display changes 100 times in one pass, SproutCore only ever updates the display once with the final value.

Now while we should almost never need to manually trigger a run of the run loop, if your app is handling non-standard events not covered by SproutCore (for example, the IndexedDB or WebSQL events) then you must take care to start the run loop in order to flush the updates across the app.

To execute code within a run loop, simply wrap it within SC.run(). For example, a websocket message event handler would look as shown:

// …
socket.on('message', function (message) {
  SC.run( function () { 
    // Start a run of the run loop so any changes made here 
propagate
    // …
  });
});
// …

Again, the above example is a rare occurrence, only to be used when handling non-standard events that are not already handled by SproutCore.

Lastly, there are also functions provided by the run loop that allow you to invoke blocks of code in a certain execution order. Here are the four methods and a description of when you may want to use each:

  • invokeOnce(func): This invokes the given function only once in the current run. This is useful if a function can potentially be called from multiple places, but you want to ensure it only runs once.
  • invokeLast(func): This invokes the given function only once at the end of the loop after bindings have flushed and after the views have updated. This is useful if the function relies on the final value of bindings or the rendered view output.
  • invokeNext(func): This invokes the given function only once asynchronously immediately following the end of the current run loop. This is useful to defer certain expensive functions until after the browser has a chance to break JavaScript execution and repaint and reflow.
  • invokeLater(func, interval): This invokes the given function only once at some time later as determined by the interval argument. This is useful to delay execution of a function for some time and still coordinate execution with the run loop rather than using setTimeout.

Note

While we can call these methods directly on the current run loop object, which is always accessible via SC.RunLoop.currentRunLoop, it's easiest to use the same named convenience wrappers from SC.Object. For example, calling myObject.invokeLast() on an SC.Object instance.

Tip

Do not use invokeLater to fix apparent timing problems with your code! Any timing issues that occur are likely the result of improper use of computed properties, bindings, and observers and so you should review your code first. As a rule, invokeLater shouldn't be used with an interval of less than 200 ms. Otherwise, it's not really "later" in terms of how long the current block of code may possibly take to execute.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.204.5