Previous: Route Tasks

Awaiting Multiple Child Tasks (or, cancelable Promise.all, Promise.race)

ember-concurrency provides Task-aware variants of Promise.all and Promise.race, which can be used in cases where a parent task wants to wait for multiple child tasks to run to completion (or throw an error) before continuing onward. The ember-concurrency variants both have the added benefit that if the parent task is canceled (or restarts), all of the child tasks will be automatically canceled. Similarly, in the case of all(), if any of the child tasks throws an error, all other child tasks are immediately canceled.

Live Example

The example below can be started (or restarted) using either all() to wait for all child tasks to run to completion, or race() to wait for the first. Note that how, in both cases, maxConcurrency: 3 ensures that only 3 progress tasks run at a time, but if you restart the task while it's running, it immediately starts 3 tasks after canceling the previous ones.

Status: Waiting...

import { task, timeout, all, race } from 'ember-concurrency';
const methods = { all, race };

export default class JoiningTasksController extends Controller {
  childTasks = null;
  colors = ['#ff8888', '#88ff88', '#8888ff'];
  status = 'Waiting...';

  @task({ restartable: true })
  *parent(methodName) {
    let allOrRace = methods[methodName];
    let childTasks = [];

    for (let id = 0; id < 5; ++id) {

    this.set('childTasks', childTasks);
    this.set('status', 'Waiting for child tasks to complete...');
    let words = yield allOrRace(childTasks);
    this.set('status', `Done: ${makeArray(words).join(', ')}`);

  @task({ enqueue: true, maxConcurrency: 3 })
  child = {
    percent: 0,
    id: null,

    *perform(id) {
      this.set('id', id);
      while (this.percent < 100) {
        yield timeout(Math.random() * 100 + 100);
        let newPercent = Math.min(
          Math.floor(this.percent + Math.random() * 20)
        this.set('percent', newPercent);
      return randomWord();
  <button {{on "click" (perform this.parent "all")}} type="button">all()</button>
  <button {{on "click" (perform this.parent "race")}} type="button">race()</button>

Previous: Route Tasks