Previous: Derived State
Next: Testing & Debugging
In addition to pausing the execution of a task until a yielded promise resolves, it's also possible to pause the execution of a task until some other event or condition occurs by using the following helpers:
The patterns described below can be seen as alternatives to using observers and other patterns that are often prone to abuse; proper use of observers or Ember.Evented hooks often requires conditionals and defensive programming to ensure that the app is in some expected state before running the logic in that hook, which is the kind of error-prone busywork that ember-concurrency intends to prevent.
By expressing event-driven code within tasks using the patterns below, you are already heavily constraining the conditions in which that event or observer can fire, which means you don't have to write nearly as much guard/cleanup logic to ensure that an event doesn't fire at an unexpected time/state.
You can use waitForEvent(object, eventName)
to pause your task until
an Ember.Evented or DOM / jQuery Event fires.
object
must include Ember.Evented (or support .one()
and .off()
) or be a valid DOM EventTarget (or support
.addEventListener()
and .removeEventListener()
).
This is useful for when you want to dynamically wait for an event to fire within a task, but you don't want to have to set up a Promise that resolves when the event fires.
Try clicking around the page; waitForEvent
will install
handlers and wait for the specified Ember, DOM or jQuery event to fire;
the value returned from yield
is the event that was fired.
domEvent = null; @task *domEventLoop() { while (true) { let event = yield waitForEvent(document.body, 'click'); this.set('domEvent', event); this.trigger('fooEvent', { v: Math.random() }); } } jQueryEvent = null; @task *jQueryEventLoop() { let $body = $('body'); while (true) { let event = yield waitForEvent($body, 'click'); this.set('jQueryEvent', event); } } emberEvent = null; @task *emberEventedLoop() { while (true) { let event = yield waitForEvent(this, 'fooEvent'); this.set('emberEvent', event); } } didInsertElement() { super.didInsertElement(...arguments); this.domEventLoop.perform(); this.jQueryEventLoop.perform(); this.emberEventedLoop.perform(); this.waiterLoop.perform(); }
<h4> domEvent: (x={{this.domEvent.offsetX}}, y={{this.domEvent.offsetX}}) </h4> <h4> jqueryEvent: (x={{this.jQueryEvent.offsetX}}, y={{this.jQueryEvent.offsetX}}) </h4> <h4> emberEvent: (v={{this.emberEvent.v}}) </h4>
Sometimes, it's desirable to know whether a certain part of a task
is executing, rather than the whole task. For instance, in the example
below, the waiterLoop
task has an infinite loop, so
waiterLoop.isRunning
will always be true. If you want
to know whether a specific part of that loop is running, then you
can just extract some of that logic into another task and check
if that subtask's isRunning
property in the template.
@task *waiterLoop() { while (true) { yield this.waiter.perform(); yield timeout(1500); } } @task *waiter() { let event = yield waitForEvent(document.body, 'click'); return event; }
<h4> {{#if this.waiter.isRunning}} Please click somewhere... {{else}} Thanks! {{/if}} </h4>
You can use waitForProperty(object, property, callbackOrValue)
to pause your task until a property on an Ember Object becomes a certain value.
This can be used in a variety of use cases, including coordination execution
between concurrent tasks.
@task *startAll() { this.set('bazValue', 1); this.set('state', 'Start.'); this.foo.perform(); this.bar.perform(); this.baz.perform(); } @task *foo() { yield timeout(500); } @task *bar() { yield waitForProperty(this, 'foo.isIdle'); this.set('state', `${this.state} Foo is idle.`); yield timeout(500); this.set('bazValue', 42); this.set('state', `${this.state} Bar.`); } bazValue = 1; @task *baz() { let val = yield waitForProperty(this, 'bazValue', (v) => v % 2 === 0); yield timeout(500); this.set('state', `${this.state} Baz got even value ${val}.`); }
<button {{on "click" (perform this.startAll)}} type="button"> Start </button> <h5> State: {{this.state}} </h5>
Previous: Derived State
Next: Testing & Debugging