ember-concurrency — part 1 of essential Ember addons for leveling up

Many of Ember’s most popular addons didn’t make sense to me as a beginner because I had not experienced firsthand the problems that they try to solve. A year later, here’s a tour of some addons I can’t live without, when to use them, and why they are needed:

  • ember-concurrency
  • ember-browserify (coming soon)
  • ember-test-selectors (coming soon)

ember-concurrency

Ember-concurrency helps you to handle two major problems: impatient users and long-lived code.

I read five articles (1 2 3 4 5 ), watched 3 talks, and went to hell & back with async code before I finally said one day, “holy ****. I understand now. How did anyone write maintainable, testable code without this!??” I know I definitely didn’t. What changed was, I understood the problem better. So, I made this demo to help show a problem in action and the ember-concurrency solution.

Managing repeated user inputs with ember-concurrency

When to use it: If a user clicked a button 20 times in 2 seconds, would you want just 1 API call to happen? Do you use anything at all with async in your app and still be able to use tests? Do you stream anything like mic audio, video, images, sensor data, etc? It’s for code that usually has a life of its own once it starts running. ember-concurrency lets you say “STOP RUNNING IN THE HOUSE. I’m your mother, you will do as I say!”

Why you need it: So far I have experienced one major immediate benefit in my own app. Any functions that start on an endless loop when a component is rendered will end when I move to another route. This addon brings many more benefits, but even if this was all it did, I’d be using it. This simple demo using Ember Twiddle (no need to download anything) demonstrates the endless loop problem. You need to understand the problem before this addon makes sense.

Here’s a different, real-world example from my codebase. This function fetches new records have been created by other users, and it runs every 10 seconds on a loop. I just import task from ember-concurrency, and put my code inside the special task(function * () {...}) syntax. When the page is loading, I start the task with this.get('pollServerForChanges').perform(). Without ember-concurrency, I’d still be pinging my API every 5 seconds even when the user is no longer on the route that needs that data. I’d be left to my own devices to figure out how to make it stop looping. And handling async is not my strong suit.

Infinite loops keep going after route transition, unless you use ember-concurrency
import { task, timeout } from 'ember-concurrency';
...
pollServerForChanges: task(function * () {
while (true) {
let lastUpdate = this.get('lastUpdate') // a timestamp
this.store.query('item', {created_at_start: lastUpdate})
yield timeout(5000); // wait 5 seconds before checking again
}
}),
...

You can check out some more real-world use case demos under the “Examples” section of ember-concurrency’s documentation.

Next let’s talk about testing. When you run a test in Ember, it sets up a whole bunch of files, and then it kills them when the test is done. This is all fine and dandy unless you have some async code(and you’re not using ember-concurrency). The things that happen after the async could become undead zombie code. They keeps running . But since everything around it was destroyed in the apocalypse, variables the zombie code tries to use may no longer be available. Your test results in “Uncaught Error: Assertion Failed: calling set on destroyed object.” However, when you use an ember-concurrency task, it is like a shotgun. It makes sure that the zombie code is stopped in its tracks at the end of a test. Now, if you have an infinite loop running (such as the code block I posted above), you still need to do a little bit of work so that your test doesn’t hang. But it’s straightforward.

Best of all, unlike life before ember-concurrency, the resulting test-friendly asynchronous code is also people friendly. You don’t have to understand how it works in order to benefit from it.

So what else can you do with this addon? You can cancel things like an audio stream. You can prevent a user’s rapid fire clicking from making your app explode (or an animation restart). You can check to see if a long lived function is still running — no need for manually setting status flags like isStreaming: true. And, you can do all these things without needing to pass around objects through callbacks. Amazing.

Who leads the project: Ember core team emeritus Alex Matchneer (Twitter and GitHub), supported by 30 other open source contributors. Special thanks for proofreading this post for accuracy.