{"id":15015809,"url":"https://github.com/eoinkelly/ember-runloop-handbook","last_synced_at":"2025-04-06T19:13:13.673Z","repository":{"id":19999138,"uuid":"23266544","full_name":"eoinkelly/ember-runloop-handbook","owner":"eoinkelly","description":"A deep dive into the Ember JS runloop.","archived":false,"fork":false,"pushed_at":"2019-05-20T23:50:47.000Z","size":853,"stargazers_count":369,"open_issues_count":0,"forks_count":33,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-30T17:11:54.244Z","etag":null,"topics":["ember","ember-application","ember-framework","ember-runloop","emberjs","javascript","runloop"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eoinkelly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-08-23T21:22:21.000Z","updated_at":"2024-10-07T17:14:13.000Z","dependencies_parsed_at":"2022-08-28T05:30:12.954Z","dependency_job_id":null,"html_url":"https://github.com/eoinkelly/ember-runloop-handbook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoinkelly%2Fember-runloop-handbook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoinkelly%2Fember-runloop-handbook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoinkelly%2Fember-runloop-handbook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoinkelly%2Fember-runloop-handbook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eoinkelly","download_url":"https://codeload.github.com/eoinkelly/ember-runloop-handbook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535519,"owners_count":20954576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ember","ember-application","ember-framework","ember-runloop","emberjs","javascript","runloop"],"created_at":"2024-09-24T19:47:58.807Z","updated_at":"2025-04-06T19:13:13.652Z","avatar_url":"https://github.com/eoinkelly.png","language":"JavaScript","funding_links":[],"categories":["Tutorials","Packages"],"sub_categories":["Books"],"readme":"# Ember runloop handbook\n\nby [Eoin Kelly](https://twitter.com/eoinkelly)\n\n![Creative Commons License](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)\n\n# Table of contents\n\n- [Contributing](#contributing)\n- [Introduction](#introduction)\n    - [Naming is hard](#naming-is-hard)\n- [Why do we have a runloop?](#why-do-we-have-a-runloop)\n- [Enter the Ember!](#enter-the-ember)\n    - [Things we already know](#things-we-already-know)\n    - [Where does the framework end and my app begin](#where-does-the-framework-end-and-my-app-begin)\n    - [What events does Ember listen to?](#what-events-does-ember-listen-to)\n    - [How ember listens for events](#how-ember-listens-for-events)\n    - [Example: A simplistic approach](#example-a-simplistic-approach)\n    - [Enter the runloop](#enter-the-runloop)\n    - [How it works](#how-it-works)\n    - [How often do runloops happen?](#how-often-do-runloops-happen)\n    - [Visualising the runloop for yourself](#visualising-the-runloop-for-yourself)\n    - [Enough with the mousemove already!](#enough-with-the-mousemove-already)\n    - [What are _autoruns_?](#what-are-autoruns)\n    - [How is runloop behaviour different when testing?](#how-is-runloop-behaviour-different-when-testing)\n- [How do I use the runloop?](#how-do-i-use-the-runloop)\n    - [A note about future work](#a-note-about-future-work)\n    - [A note about rate control](#a-note-about-rate-control)\n- [Summary](#summary)\n- [Appendices](#appendices)\n    - [Sources](#sources)\n    - [Other resources on the Runloop](#other-resources-on-the-runloop)\n\n# Contributing\n\nIf you spot any of the (sadly inevitable) errors you would be doing me a great\nfavour by opening an issue :-).\n\n# Introduction\n\nYou can get started with Ember application development without understanding the\nrunloop. However at some point you will want to dig in and understand it\nproperly so you can use it skillfully. It is my sincere hope that this handbook\ncan be your guide.\n\nWe are about to take a deep dive into the Ember.js runloop. Together we will\nanswer these questions:\n\n1. Why does Ember have this runloop thing?\n2. What is the runloop?\n3. How can we use it skillfully?\n\nThis is not reference documentation - the [Ember API\ndocs](http://emberjs.com/api/) have that nicely covered. This isn't even the\n_\"I'm an experienced dev, just give me the concepts in a succinct way\"_\ndocumentation - the [Official Ember Run-loop\nguide](https://guides.emberjs.com/v2.11.0/applications/run-loop) has that\ncovered. This is a longer, more detailed look at the runloop.\n\n## Naming is hard\n\nAs you learn more about the Ember runloop you will come to understand that,\nwell, it just isn't very _loopish_. The name is a bit unfortunate as it\nimplies that there is a single instance of the runloop sitting somewhere in\nmemory looping endlessly _running things_. As we will see soon this is not\ntrue.\n\nIn alternate universes the _runloop_ might have been named:\n\n* _Ember Work Queues_\n* _Ember Coordinated Work Algorithm_\n* _Ember Job Scheduler_\n* _Runelope (a large friendly creature that lives in your Javascript VM and\n  manages the work Ember does in response to events)_\n\nOK some of those names are really terrible (except _Runelope_ of course, that\none is pure gold and should be immediately pushed to Ember master). Naming is a\nhard problem and hindsight is 20/20. The _runloop_ is what we have so that is\nwhat we will call it but try not to infer too much about its action from its\nname.\n\n# Why do we have a runloop?\n\nOn our journey to understand the runloop we must first understand the\nenvironment it lives in and the problems it is trying to solve.  Lets set the\nscene by refreshing a few fundamentals about how Javascript runs. (If you are an\nexperienced Javascript developer you may want to just skip this part)\n\nOur story begins with when the browser sends a request to the server and the\nserver sends HTML back as a response.\n\nThe browser then parses this HTML response. Every time it finds a script it executes it\nimmediately(*) Lets call this the _setup phase_.  This _setup phase_ happens\nwell before the user sees any content or gets a chance to interact with the DOM.\nOnce a script is finished executing the browser never runs it again.\n\n(*) Things like `defer` tweak this somewhat but this is a useful simplification.\n\nThe browser does most of its communication with Javascript by sending \"events\".\nUsually these are created in response to some action from one of:\n\n1. The user e.g. moves their mouse (`mousemove`)\n2. The network e.g. an asset has been loaded on the page (`load`)\n3. Internal timers e.g. a particular timer has completed\n\nHowever there are a few events that the browser generates itself to tell\nJavascript about some important event in the lifecycle of the page. The most\nwidely used of these is `DOMContentLoaded` which tells Javascript that the HTML\nhas been fully parsed and the DOM (the memory structure the browser builds by\nparsing the HTML) is complete. This is significant for Javascript because it\ndoes most of its setup work in response to this event.\n\nJavascript is lazy but well prepared!  During the _setup phase_, Javascript prepared its work\nspace (or _mise en place_ if you prefer) - it created the objects it would now need\n to respond to orders (events) from the browser and also told the browser\nin detail what events it cares about e.g.\n\n\u003e Hey browser, wake me up and run this function I'm giving you whenever the user\n\u003e clicks on an element with an id attribute of `do-stuff`.\n\nThe description above makes it look like the browser is the one giving all the\norders but the browser is a team player and has a few things it can do to help\nJavascript get the job done:\n\n1. Timers. Javascript can use the browser like an alarm clock:\n\n    \u003e Javascript: Hey browser, wake me up and run this function I'm giving you in 5 seconds please.\n\n2. Talking to other systems. If Javascript needs to send or receive data to other\ncomputers it asks the browser to do it:\n\n    \u003e Javascript: Hey browser, I want to get whatever data is at\n    \u003e `http://foo.com/things.json` please.\n\n    \u003e Browser: Sure thing but it might take a while. What do you want me to do\n    \u003e when it comes back?\n\n    \u003e Javascript: I have two functions ready to go (one for a successful data fetch and\n    \u003e one for a failure) so just wake me up and run the appropriate one when you\n    \u003e finish.\n\n    \u003e Browser: cool.\n\nWe usually refer to this _talking to other systems_ stuff as Web APIs e.g.\n\n* XHR (AJAX) requests\n* Web workers\n* etc.\n\nJavascript can use these services of the browser both during the setup phase\nand afterwards. For example part of the Javascript response to a \"click\" event on a\ncertain element might be to retrieve some data from the network and also\nschedule a timer to do some future work.\n\nWe now know enough to see the pattern of how javascript and the browser\ninteract and to understand the two phases:\n\n1. In the short _setup phase_ the browser runs each script it finds on the page\nfrom start to finish. Javascript uses this as time to do some preparation for next phase.\n2. Javascript spends most of its life _responding to events_. Many events\ncome from the user but Javascript can also schedule events for itself by using\nthe many services (web APIs) that the browser provides.\n\nA solid understanding of this stuff is required to understand the runloop so if\nyou are unclear about any of this and want to dig a little deeper I recommend a\n[wonderful video by Philip Roberts at JSConf EU](https://youtu.be/8aGhZQkoFbQ)\nthat goes into the Javascript event loop in more detail. It is a short watch and includes\na few \"aha!\"-inducing diagrams.\n\n# Enter the Ember!\n\n## Things we already know\n\nSince Ember is Javascript we already know quite a bit about how Ember works:\n\n* Apart from when the code is first found, all Ember framework and application\n  code is run in response to \"events\" from the browser.\n* The `DOMContentLoaded` event is significant in the life of an Ember app. It tells\n  Ember that it now has a full DOM to play with. Ember will do most of its \"setup work\"\n  (registering for event listeners etc.) in response to this event.\n* Your Ember app can schedule its own events by asking the browser to do some work\n  on its behalf (e.g. AJAX requests) or simply by asking the browser to be its\n  alarm clock (e.g. `setTimeout`)\n\n## Where does the framework end and my app begin\n\nHow does your Ember _application_ relate to the Ember _framework_? The machinery\nfor responding to events is part of Ember _framework_ itself but it does not\nhave a meaningful response without your _application_ code.\n\nFor example if the user is on `/#/blog/posts` and clicks a link to go to\n`/#/authors/shelly` the Ember _framework_ will receive the click event but it\nwon't be able to do anything meaningful with it without:\n\n1. A Router map to tell it how to understand the URL\n2. The Route objects themselves e.g. `BlogRoute`, `PostsRoute`, `AuthorsRoute`\n3. The models, controllers, views that all play a part in putting new data on the screen\n\n## What events does Ember listen to?\n\nThe Ember docs have a list of [events Ember listens for by\ndefault](http://emberjs.com/api/classes/Ember.View.html#toc_event-names) which I have repeated here:\n\n1. touchStart\n2. touchMove\n3. touchEnd\n4. touchCancel\n5. keyDown\n6. keyUp\n7. keyPress\n8. mouseDown\n9. mouseUp\n10. contextMenu\n11. click\n12. doubleClick\n13. mouseMove\n14. mouseEnter\n15. mouseLeave\n16. submit\n17. change\n18. focusIn\n19. focusOut\n20. input\n21. dragStart\n22. drag\n23. dragEnter\n24. dragLeave\n25. dragOver\n26. dragEnd\n27. drop\n\nThese are the entry points into our code. Whenever Ember code runs after the\nsetup phase, it is in response to an event from this list.\n\n## How ember listens for events\n\n[This](http://www.quirksmode.org/js/events_order.html) is a good resource for\nrefreshing your understanding of how DOM events work. To get the most of the\nfollowing discussion you should be familiar with how the browser propagates\nevents and what the phrases \"capturing phase\" and \"bubbling phase\" mean.\n\nEmber registers listeners for these events similarly to how we might do it\nourselves with jQuery i.e.\n\n* Ember attaches *all* its listeners to a single element in the DOM.\n* This element is usually `\u003cbody\u003e`. If you specify a `rootElement` then that will be used instead.\n* Ember attaches its listeners to the \"bubbling\" phase.\n\n## Example: A simplistic approach\n\nThe pattern of how Javascript (Ember) works is periods of intense activity in\nresponse to some event followed by idleness until the next event happens. Lets\ndig a little deeper into these periods of intense activity.\n\nWe already know that the first code to get run in response to an event is the\nlistener function that Ember registered with the browser. What happens after\nthat?\n\nLets consider some code from an imaginary simple Javascript app:\n\n[http://jsbin.com/diyuj/5/edit?html,js,console,output](http://jsbin.com/diyuj/5/edit?html,js,console,output)\n\nThis code manages the \"Mark all completed\" button in the UI.\n\nClick the button a few times and notice the console output. Notice that there\nare some patterns to the tasks performed:\n\n1. Updating the model\n2. Updating the DOM (rendering)\n\nand that the _do work as you find it_ approach that this app takes causes these\ndifferent types of work to be interleaved.\n\nThe code in this app is obviously very incomplete and I'm sure you can see many\nways it could be improved. However there are some problems that might not be\nobvious at first, problems that you will only start to notice when the app\ngrows in complexity. To understand these lets look at what it is _not_ doing:\n\n1. It is _not coordinating its access of the DOM_. Every time the app updates\n   the DOM the browser does a layout and paint. These are very expensive\n   operations especially on mobile devices.\n2. It has _no way of telling us when DOM updating is finished_. We can certainly\n   hook into the click handler for the \"Mark all completed\" button but what if\n   had started some asynchronous work like updating the server? If this app was\n   more realistic it would be very difficult to know where we should add code\n   that would be run when all DOM updates had finished.\n3. It is not controlling _when_ objects get deleted. Currently our app is so\n   trivial that this is not a problem but imagine if we had hundreds of todo\n   items and complex processing of each one i.e. processing each todo item\n   created a lot of temporary objects in memory. After a while the browser\n   will decide that enough is enough and that it needs to \"clean up\" these\n   objects and make their memory available again i.e. it will run garbage\n   collection. Since our app cannot run while GC is happening the user may\n   notice a pause.\n\nTogether these problems mean our simplistic Todo app will have serious scaling problems.\n\n## Enter the runloop\n\nWe have identified some problems caused by an uncoordinated approach to doing\nwork. How does Ember solve them?\n\nInstead of doing work as it finds it, Ember schedules the work on an internal\nset of queues. By default Ember has six queues:\n\n```js\nconsole.log(Ember.run.queues);\n// [\"sync\", \"actions\", \"routerTransitions\", \"render\", \"afterRender\", \"destroy\"]\n```\n\nEach queue corresponds to a \"phase of work\" identified by the Ember core team.\nThis set of queues and the code that manages them **is** the Ember runloop.\n\nYou can see a summary of the purpose of each queue in the [Runloop\nGuide](https://guides.emberjs.com/v2.11.0/applications/run-loop/#toc_an-example-of-the-internals)\nbut here we are going to focus on the queues themselves.\n\n## How it works\n\nFirst lets get some terminology sorted:\n\n* A _job_ on a queue is just a plain ol' Javascript callback function.\n* _Running a job_ is simply executing that function.\n\nHow Ember handles events:\n\n1. A browser event happens and Ember's registered listener for that event is triggered.\n2. Early on in its response to the event, Ember opens a set of queues and starts\n   accepting jobs.\n3. As Ember works its way through your application code, it continues to\nschedule jobs on the queues.\n4. Near the end of its response to the event Ember closes the queue-set and starts\nrunning jobs on the queues. Scheduled jobs can themselves still add jobs to the queues even\n   though we have closed them to other code.\n5. The [Runloop Guide](https://guides.emberjs.com/v2.11.0/applications/run-loop/#toc_an-example-of-the-internals)\n   has an excellent visualisation of how jobs are run but in brief:\n    1. Scan the queues array, starting at the first until you find a job. Finish if all queues are empty.\n    2. Run the job (aka execute the callback function)\n    3. Go to step 1\n\nLets consider some subtle consequences of this simple algorithm:\n\n* Ember does a full queue scan after each *job* - it does not attempt to finish\n  a full queue before checking for earlier work.\n* Ember will only get to jobs on a queue if all the previous queues are empty.\n* Ember cannot *guarantee* that, for example, *all* `sync` queue tasks will be\n  complete before any `actions` tasks are attempted because jobs on any queue\n  after `sync` might add jobs to the `sync` queue. Ember will however do its\n  best to do work in the desired order. It is not practical for your app to\n  schedule *all* work before any is performed so this flexibility is necessary.\n* At first glance it may seem that the runloop has two distinct phases\n\n    1. Schedule work\n    2. Perform the work\n\n    but this is subtly incorrect. Functions that have been scheduled on a runloop queue\n    can themselves schedule functions on **any** queue in the same runloop. It is\n    true that once the runloop starts executing the queues that code **outside** the\n    queues cannot schedule new jobs. In a sense the initial set of jobs that are\n    scheduled are a \"starter set\" of work and Ember commits to doing it and also\n    doing any jobs that result from those jobs - Ember is a pretty great\n    employee to have working for you!\n\nSomething that is not obvious from that description is that there is no\n\"singleton\" runloop. This is confusing because documentation (including this\nguide) uses the phrase \"the runloop\" to refer to the whole system but it is\nimportant to note that there is not a single instance of the runloop in memory\n(unlike the [Ember container](https://guides.emberjs.com/v2.11.0/applications/dependency-injection/)\nwhich is a singleton). There is no \"the\" runloop, instead there can be multiple\ninstances of \"a\" runloop. It is true that Ember will usually only create one\nrunloop per DOM event but this is not always the case. For example:\n\n* When you use `Ember.run` (see below) you will be creating your own\n  runloop that may go through its full lifecycle while the runloop that Ember\n  uses is still accepting jobs.\n* Usually an Ember application will boot within a single runloop but if you\n  enable the [Ember Inspector](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi?hl=en) then many more runloops happen at boot time.\n\nAnother consequence of the runloop not being a singleton is that it does not\nfunction as a \"global gateway\" to DOM access for the Ember app. It is not\ncorrect to say that the runloop is the \"gatekeeper\" to all DOM access in Ember,\nrather that \"coordinated DOM access\" is a pleasant (and deliberate!) side-effect\nof organising all the work done in response to an event.. As mentioned above,\nmultiple runloops can exist simultaneously so there is no guarantee that *all*\nDOM access will happen at one time.\n\n## How often do runloops happen?\n\nFrom what I have observed, Ember typically runs one runloop in response to each\nDOM event that it handles.\n\n## Visualising the runloop for yourself\n\nThis repo also contains the [noisy runloop kit](https://github.com/eoinkelly/ember-runloop-handbook/tree/master/noisy-runloop-kit) which is trivial demo app and\na copy of Ember that I have patched to be very noisy about what its runloop\ndoes. You can add features to the demo app and see how the actions the runloop takes in\nresponse in the console. You can also use the included version of Ember in your own\nproject to visualise what is happening there. Obviously you should only include\nthis in development because it will slow the runloop down a lot.\n\n## Enough with the mousemove already!\n\nWhen you start getting the runloop to log its work you will quickly get\noverwhelmed by its running in response to mouse events that happen very\nfrequently on desktop browsers e.g. `mousemove`. Below is an initializer for\nEmber that will stop it listening to certain events. You probably want to add\nthis to whatever Ember app you are trying to visualise the runloop for unless\nyou are actually _using_ `mousemove`, `mouseenter`, `mouseleave` in your app.\n\n```js\n/**\n * Tell Ember to stop listening for certain events. These events are very\n * frequent so they make it harder to visualise what the runloop is doing. Feel\n * free to adjust this list by adding/removing events. The full list of events\n * that Ember listens for by default is at\n * http://emberjs.com/api/classes/Ember.View.html#toc_event-names\n *\n */\n\nEmber.Application.initializer({\n  name: 'Stop listening for overly noisy mouse events',\n\n  initialize: function(container, application) {\n    var events = container.lookup('event_dispatcher:main').events;\n    delete events.mousemove;\n    delete events.mouseenter;\n    delete events.mouseleave;\n  }\n});\n```\n\n## What are _autoruns_?\n\nCalls to any of\n\n* `run.schedule`\n* `run.scheduleOnce`\n* `run.once`\n\nhave the property that they will approximate a runloop for you if one does not\nalready exist. These automatically (implicitly) created runloops are called\n_autoruns_.\n\nLets consider an example of a click handler:\n\n```js\n$('a').click(function(){\n  console.log('Doing things...');\n\n  Ember.run.schedule('actions', this, function() {\n    // Do more things\n  });\n  Ember.run.scheduleOnce('afterRender', this, function() {\n    // Yet more things\n  });\n});\n```\n\nWhen you call `schedule` Ember notices that there is not a currently open\nrunloop so it opens one and schedules it to close on the next turn of the JS\nevent loop.\n\nHere is some pseudocode to describe what happens:\n\n```js\n$('a').click(function(){\n  // 1. autoruns do not change the execution of arbitrary code in a callback.\n  //    This code is still run when this callback is executed and will not be\n  //    scheduled on an autorun.\n  console.log('Doing things...');\n\n  Ember.run.schedule('actions', this, function() {\n    // 2. schedule notices that there is no currently available runloop so it\n    //    creates one. It schedules it to close and flush queues on the next\n    //    turn of the JS event loop.\n    if (! Ember.run.hasOpenRunloop()) {\n      Ember.run.start();\n      nextTick(function() {\n          Ember.run.end()\n      }, 0);\n    }\n\n    // 3. There is now a runloop available so schedule adds its item to the\n    //    given queue\n    Ember.run.schedule('actions', this, function() {\n      // Do more things\n    });\n\n  });\n\n  // 4. scheduleOnce sees the autorun created by schedule above as an available\n  //    runloop and adds its item to the given queue.\n  Ember.run.scheduleOnce('afterRender', this, function() {\n    // Yet more things\n  });\n\n});\n```\n\nAlthough autoruns are convenient you should not rely on them because:\n\n1. The current JS frame is allowed to end before the run loop is flushed, which\nsometimes means the browser will take the opportunity to do other things, like\ngarbage collection.\n2. Only calls to `run.schedule`, `run.scheduleOnce` and `run.once` are wrapped\nin autoruns.  All other code in your callback will happen outside Ember. This\ncan lead to unexpected and confusing behavior.\n\n## How is runloop behaviour different when testing?\n\nWe know that\n\n* `run.schedule`\n* `run.scheduleOnce`\n* `run.once`\n\ncreate a new runloop if one does not exist and that these automatically\n(implicitly) created runloops are called _autoruns_.\n\nIf `Ember.testing` is set then this _\"automatic runloop approximation creation\"_\nbehaviour is disabled. In fact when `Ember.testing` is set these three functions\nwill throw an error if you run them at a time when there is not an existing\nrunloop available.\n\nThe reasons for this are:\n\n1. Autoruns are Embers way of not punishing you in production if you forget to\nopen a runloop before you schedule callbacks on it. While this is useful in\nproduction, these are still issues you should fix and are revealed as such in\ntesting mode to help you find and fix them.\n2. Some of Ember's test helpers are promises that wait for the run loop to empty\nbefore resolving. If your application has code that runs _outside_ a runloop,\nthese will resolve too early and gives erroneous test failures which can be\n**very** difficult to find. Disabling autoruns help you identify these scenarios\nand helps both your testing and your application!\n\n\n# How do I use the runloop?\n\nThe [Ember runloop API docs](http://emberjs.com/api/classes/Ember.run.html) are\nthe canonical resource on what each function does. This section will provide a\nhigh-level overview of how the API works to make it easier to categorise it in\nyour head and put it to use.\n\nIn the API we have:\n\n* 1 way of running a given callback in a new runloop\n    * `Ember.run`\n* 1 way of adding a callback to the currently open runloop\n    * `Ember.run.schedule`\n* 2 ways to add a callback to the current runloop and ensure that it is only added once.\n    * `Ember.run.scheduleOnce`\n    * `Ember.run.once`\n* 2 ways to add a callback to some future runloop\n    * `Ember.run.later`\n    * `Ember.run.next`\n* 2 ways of doing rate control on a callback. These control how often a callback is called (it will get its own runloop each time)\n    * `Ember.run.debounce`\n    * `Ember.run.throttle`\n* 1 way of cancelling work scheduled for a future runloop or rate control\n    * `Ember.run.cancel`\n* 2 functions provide a low-level alternative to `Ember.run`\n    * `Ember.run.begin`\n    * `Ember.run.end`\n* 1 convenience function for forcing bindings to settle\n    * `Ember.run.sync`\n\n| Function | Which runloop? | Which queue? | Creates new runloop? | Notices `Ember.testing`? | Runs callback in current JS event loop turn?\n| ----------------------------- | -------------------------- | -------- | ----------- | ----------- |----------- |\n| `Ember.run`\t\t            | always-new | `actions` | Always | No | Yes |\n| `Ember.run.debounce`\t\t    | always-new | `actions` | Always | No | No |\n| `Ember.run.throttle`\t\t    | always-new | `actions` | Always | No | No |\n| `Ember.run.join`\t\t        | current | `actions` | If required | No | Yes |\n| `Ember.run.bind`\t\t        | current | `actions` | If required | No | No |\n| `Ember.run.schedule`\t\t    | current | chosen by param | If required | Yes | Yes |\n| `Ember.run.scheduleOnce`\t\t| current | chosen by param| If required | Yes | Yes |\n| `Ember.run.once`\t\t        | current | `actions` | If required | Yes | No |\n| `Ember.run.later`\t\t        | future | `actions` | If required | Yes | No |\n| `Ember.run.next`\t\t        | future | `actions` | If required | Yes | No |\n| `Ember.run.begin`\t\t        | NA | NA | Never | No | NA |\n| `Ember.run.end`\t\t        | NA | NA | Never | No | NA |\n| `Ember.run.cancel`\t\t    | NA | NA | NA | NA | NA |\n| `Ember.run.sync`\t\t        | NA | NA | NA | NA | NA |\n\n\nLegend:\n\n* future = some runloop in the future\n* The default queue in Ember is `actions`\n* NA = not applicable\n\n\n## A note about future work\n\nThere are two functions in the runloop API that let us schedule \"future work\":\n\n1. `Ember.run.later`\n1. `Ember.run.next`\n\nEach of these API functions is a way of expressing _when_ you would like work\n(a callback function) to happen. The guarantee provided by the runloop is that\nit will also manage the other work that results from running that function. It\ndoes not guarantee anything else!\n\nThe key points:\n\n* Ember keeps an internal queue of \"future work\" in the form of an array of\n  timestamp and function pairs e.g. `[(timestamp, fn), (timestamp, fn) ... ]`\n* It uses this queue to manage _work you have asked it to do on some runloop that is not the current one_.\n* Each of the API functions above is a different way of adding a `(timestamp,\n  callback)` pair to this array.\n* Ember does not know exactly when it will get a chance to execute this future\n  work (Javascript might be busy doing something else).\n* Each time it checks the timers queue it executes all the functions whose timestamps\n  are in the past so the future work API functions are creative in their\n  creation of timestamps to achieve what they want.\n* When Ember does find some pairs on the _future work queue_ that should be\n  executed it creates a new runloop (using `Ember.run`) and schedules each\n  function onto the `actions` queue.\n\nConsequences:\n\n* When you give a function to one of the future work API functions you cannot\n  know which runloop it will run in!\n    * It may share a runloop with other future work functions.\n    * It will only share with other functions from the future work queue\n      - it will not share a runloop with other Ember code or anything you\n      explicitly pass to `Ember.run` yourself.\n* You can only directly schedule future work onto the `actions` queue. If you need to run\n  something on a different queue of that future runloop you will need to\n  schedule it _from_ that `actions` queue callback.\n* Future work APIs let you specify _some_ future runloop but not exactly which\n  one.\n\n## A note about rate control\n\nEmber provides two flavors of rate control.\n\n* `Ember.run.debounce`\n    * Ignore a callback if the previous call to it was within a given time period\n* `Ember.run.throttle`\n    * Used to guarantee a minimum time between calls to a particular callback\n\nThese functions are useful because they allow us to control when the given\ncallback is _not_ run. When it is actually run, these functions use `Ember.run`\nso these functions can be thought of  as \"`Ember.run` with some extra controls\nabout when the function should be run\"\n\n# Summary\n\nIt can take a while to get our heads around the subtleties of the runloop. In\nexchange we get the performance and scaling benefits that the runloop provides.\nI hope that you now feel more equipped to use the runloop skillfully.\n\nHappy hacking.\n\n# Appendices\n\n## Sources\n\nThe primary documentation for the Ember runloop is [Official Ember Run-loop\nguide](http://emberjs.com/guides/understanding-ember/run-loop/) and the [Ember\nAPI docs](http://emberjs.com/api/)\n\nThese are other sources I studied in compiling this guide:\n\n* [Ember source code](https://github.com/emberjs/ember.js)\n* Books\n    * [Developing an Ember Edge](http://bleedingedgepress.com/our-books/developing-an-ember-edge/)\n    * [Ember.js in Action](http://www.manning.com/skeie/)\n\n## Other resources on the Runloop\n\n* [The Ember Run Loop by Jason Madsen at Salt Lake City Ember Meetup](https://youtu.be/G4DdNMLubgQ)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feoinkelly%2Fember-runloop-handbook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feoinkelly%2Fember-runloop-handbook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feoinkelly%2Fember-runloop-handbook/lists"}