Task Function Syntax

When a task is performed, it runs the code in the task function you passed into task(). This function must be a generator function — it must use the function * syntax, and cannot be just a regular JavaScript function.

This example demonstrates how, in ember-concurrency, generator functions behave just like regular functions. Anything you can do in a regular function, you can do in a generator function.

pickRandomNumbers: task(function * () {
  let nums = [];
  for (let i = 0; i < 3; i++) {
    nums.push(Math.floor(Math.random() * 10));
  }

  this.set('status', `My favorite numbers: ${nums.join(', ')}`);
}),

Using the yield keyword

Much of the power of Tasks is unleashed once you start making use of the yield keyword within generator functions. The yield keyword, when used with a promise, lets you pause execution of your task function until that promise resolves, at which point the task function will continue running from where it had paused.

This example demonstrates how you can yield timeout(1000) to pause execution for 1000 ms (one second). The timeout() helper function, which ember-concurrency provides, simply returns a promise that resolves after the specified number of milliseconds.

waitAFewSeconds: task(function * () {
  this.set('status', "Gimme one second...");
  yield timeout(1000);
  this.set('status', "Gimme one more second...");
  yield timeout(1000);
  this.set('status', "OK, I'm done.");
}),

When you yield a promise, the yield expression evaluates to the resolved value of the promise. In other words, you can set a variable equal to a yielded promise, and when the promise resolves, the task function will resume and the value stored into that variable will be the resolved value of the promise.

myTask: task(function * () {
  this.set('status', `Thinking...`);
  let promise = timeout(1000).then(() => 123);
  let resolvedValue = yield promise;
  this.set('status', `The value is ${resolvedValue}`);
}),

If you yield a promise that rejects, the task function will throw the rejected value (likely an exception object) from the point in task function where the rejecting promise was yielded. This means you can use try {} catch(e) {} finally {} blocks, just as you would for code that runs synchronously.

myTask: task(function * () {
  this.set('status', `Thinking...`);
  try {
    yield timeout(1000).then(() => {
      throw "Ahhhhh!!!!";
    });
    this.set('status', `This does not get used!`);
  } catch(e) {
    this.set('status', `Caught value: ${e}`);
  }
}),

The behavior of yielding promises within task generator functions is designed to closely follow the behavior of the proposed async/await syntax, but instead of async function, you use function *, and instead of await, you use yield.