Using Ember.js, it is possible to handle asynchronous logic. The ember router handles asynchronous logic with the concept of promises.
Promises represent eventual values in Ember. A promise can either be fulfilled or rejected. A fulfilled promise represents a value that has been resolved, while a rejected promise represents a value that has not been resolved.
We can retrieve a promise’s eventual value or handle a rejection case using a promise’s then()
method.
The method takes two (optional) callbacks: one for fulfillment and the other for rejection. If the promise fulfills, the fulfillment method is called with the fulfilled value as its argument. If it is rejected, the rejection handler is called along with a reason for rejection.
Promises can also be chained together to perform sequential asynchronous operations.
Here’s an example that demonstrates the use of promises:
let promise = getEdpressoTagLine();
promise.then(fulfillCallback, rejectCallback);
function fulfillCallback(value) {
console.log(`The tag line is ${value}`);
}
function rejectCallback(reason) {
console.log(`Couldn't get the tag line! Reason: ${reason}`);
}
Using promises with fulfillment and rejection callbacks allows us to imitate an asynchronous form of the try-catch statements.
In the ember router, the different types of handling asynchronous logic (using promises) are as follows:
The router pauses for promises
When promises reject
Recovering from rejection
When the ember router is transitioning between routes, we can complete the transition immediately by returning normal objects or arrays from the model
. However, returning a promise from the model
hook pauses the transition.
If the promise fulfills, the transition picks up where it left off.
The following example shows this process:
export default class PausedRoute extends Route {
model() {
return new Promise(function(resolve) {
later(function() {
resolve({ msg: 'How was the wait?' });
}, 5000); // time in ms
});
}
setupController(controller, model) {
console.log(model.msg); // "How was the wait?"
}
}
Running the above code on the ember server results in a five-second wait, after which the message "How was the wait?"
is logged onto the screen.
This happens because the router pauses mid-transition until the promise is fulfilled. Since we purposefully added a
We have just learned that the router pauses the transition until the promise returned by the model
is fulfilled. However, if a model promise rejects during a transition, the transition is aborted. No new destination route templates are rendered, and an error is logged to the console.
We can choose to handle the error event using a custom error handler, or allow the error to bubble up to the route:application
's default error handler.
The following example shows this process:
export default class FailedRoute extends Route {
model() {
return Promise.reject("PROMISE REJECTED");
}
@action
error(reason) {
alert(reason); // "PROMISE REJECTED"
// this.transitionTo('somewhere');
// Uncomment the line below to bubble this error event:
// return true;
}
}
In the above example, the error event is handled and not allowed to bubble up to route:application
's error handler.
However, returning true
from the custom handler lets the error bubble up. Additionally, we can transition to another route using the transitionTo()
method.
We can catch the promise rejects within the model
hook and convert them into fulfills that do not put the transition on halt. This allows us to recover from rejection and does not abort the transition.
The following example shows this process:
export default class RecoveryRoute extends Route {
model() {
return ifRejected().catch(function() {
// Promise rejected, fulfill with some default value
return { msg: 'Recovered from rejected promise' };
});
}
}
In the above example, the promise is rejected. However, the rejected promise is caught within the model
hook and converted into a fulfill.