ember-concurrency — Better Than Promises

Tasks Manage Cancellation, Race, Drop, and State

ember-concurrency — Better Than Promises

The ember-concurrency addon wraps async work with cancellation, drop and restartable semantics, and built-in isRunning flags.

4 min read Level 3/5 #ember#concurrency#async
What you'll learn
  • Install the ember-concurrency package
  • Define a task with the task helper
  • Call perform and read the isRunning flag

ember-concurrency is the answer to “how do I cancel that fetch?” and “how do I show a spinner?” in idiomatic Ember. It wraps async work in tasks that you perform, with built-in state and cancellation.

Install

ember install ember-concurrency

Define A Task

import Component from '@glimmer/component';
import { task } from 'ember-concurrency';
import { service } from '@ember/service';

export default class Search extends Component {
  @service store;

  searchTask = task(async (query) => {
    return this.store.query('post', { q: query });
  });
}

Perform From The Template

<input {{on "input" (perform this.searchTask)}} />

{{#if this.searchTask.isRunning}}
  <Spinner />
{{else if this.searchTask.last.value}}
  <ul>
    {{#each this.searchTask.last.value as |post|}}
      <li>{{post.title}}</li>
    {{/each}}
  </ul>
{{/if}}

What A Task Gives You

Every task instance has:

  • isRunning — true while at least one perform is in flight
  • isIdle — opposite of isRunning
  • last — the most recent task instance (with .value, .error, etc.)
  • lastSuccessful, lastErrored, lastCanceled
  • performCount — how many times it has been called

These are all @tracked, so templates re-render when they change.

Cancellation

Calling task.cancelAll() cancels in-flight runs. Cancellation propagates through generator/async — pending awaits throw a cancellation error that ember-concurrency swallows.

Task Modifiers →