Ember.js Run Loop

  Ember.js has an list of named queues flushed at the end of Run Loop, uses Array to maintain the order. Understanding Backburner.js is important since @ember/runloop is mainly a wrapper around it.

Backburner.js

  Backburner.js, not a well-known library in the frontend development ecosystem, is a task scheduling library that helps manage and schedule tasks to run efficiently in web applications. Its primary purpose is to ensure that expensive or time-consuming tasks do not block the main thread, preventing the user interface from becoming unresponsive.

  Some of the capabilities of this library includes Task scheduling in specific time/order, throttle / debounce, async processing, concurrency etc. Compared to setTimeout, this provides more advanced task scheduling capabilities. As an example, it helps to schedule tasks with specific priority levels, which can be beneficial when you need fine-grained control over task execution order.

  Another feature is task Coordination, where Backburner.js offers a way to coordinate and manage concurrent tasks, ensuring that you don't run too many tasks simultaneously. This can be useful for managing limited resources or preventing excessive load on the browser.

  One last feature I would like to mention is allows you to create custom task queues for specific application needs. This can be helpful in situations where you want to manage various task types separately.

  There is a low-level API for controlling the backburner task scheduling- there is a call for begin method, followed by a matching call for end method. Between these two points, all the actions are buffered inside queue. On end, all of the queue contents are flushed. Many of the methods like join, begin, schedule etc are flexible enough to start a new runloop, unless already started.

  schedule can be used to execute a specifically named queue with a context, and later invokes the callback with a passed context unlike setTimeout which takes in the scope enclosing it.

  There are some methods which makes sense to be called inside the run method. Like join or scheduleOnce. This is because the in case of former, there is not meaning in join logic until existing RunLoop is already present. Similar for latter, the callback is schedule to be called once per RunLoop. Multiple invocations outside of it will cause new RunLoops to be created and end up being called more than once (need to verify).

Now that the runloop usage is deprecated, we are stepping towards usage of ember-lifeline. There is a replacement mapping of runloop methods to correponding alternatives in ember-lifeline.

main.js

const RUNLOOP_TO_LIFELINE_MAP = Object.freeze({
  later: 'runTask',
  next: 'runTask',
  debounce: 'debounceTask',
  schedule: 'scheduleTask',
  throttle: 'throttleTask',
});

ref:source

This library is used by @ember/-internals, as part of glimmer environment initializations to set the global context(@glimmer/global-context) using setGlobalContext. The concept of Global context is covered as part of Glimmer-VM.

There are some problems present in @ember/runloop, like async functions which are not guaranteed to be cleaned up when objects are destroyed. This is solved by ember-lifeline addon. So, the former is deprecated in favour of the latter. We also have ember-concurrency which solves this problem in a creative way, based on reactive pattern.

ember-lifeline

As the README says, the purpose of this addon is to introduce several functional utility methods to help manage async, object lifecycles, and the Ember runloop. It also mentions to associate async behaviour to object instances using functional API.

The runTask method entangles a timer with the object, where the task scheduled is cancelled if the object id destroyed before invoking the task. Else we would have to litter run.later method with this.isDestroyed checks to ensure that the task is run only when the object instance is still present.

There are functionalities for auto-removal of event listeners on object destruction - addEventListener.

References