This workshop is important because:
Promises are a powerful pattern for asynchronous behavior in JavaScript. They come up in interviews, and they're actually conceptually pretty simple. They're used a lot across libraries.
After this workshop, developers will be able to:
- Explain the purpose of promises.
- Draw the lifecycle of a promise.
- Manipulate promises using Angular’s $q service.
Before this workshop, developers should already be able to:
- Make an
$http
call, attaching success and error handlers with.then()
. - Create a simple Angular app with one controller.
Let's go over the following questions:
- What is a promise?
- What does it mean for a promise to be pending, fulfilled, resolved, and rejected?
Promises are:
- A way to pass around a value that hasn't actually been calculated yet.
- A way to attach callback-like behavior to a function call that hasn't completed yet.
- A popular interview topic.
- Anything you can
.then( ... )
on. - Something that can be pending, then gets resolved/fulfilled or rejected/failed.
Image source: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt1-theory-and-semantics
You've used promises before. Many libraries - including jQuery and Angular -- have implementations of promises.
- Can you think of an example of promises from Angular?
There are a few minor provisos. Various implementations of promises have all been a little bit different across libraries and languages. We'll dive in with Angular's $q
.
The promise library in Angular is $q
.
Deferreds are objects in Angular that represent deferred tasks. Each "promise" is the eventual result of a deferred task. These structures let our code "promise" to run the attached functions when the deferred task is finished, whether it was successful or not.
A new instance of deferred is constructed by calling
$q.defer()
. The purpose of the deferred object is to expose the associated Promise instance as well as APIs that can be used for signaling the successful or unsuccessful completion, as well as the status of the task.
function task(str){ // set up a function to use with promises
var deferred = $q.defer(); // create a new 'deferred'
// do some work...
console.log(str);
// in what case(s) should the deferred be resolved (success)?
// write code to actually **resolve** the promise in each case...
if (str === "dude" || str === "sweet"){
deferred.resolve(str); // argument gets passed to promise success
} else if (str === "Where's my car?"){
deferred.resolve("Aww man...");
}
// in what case(s) should the deferred be rejected (error)?
// write code to actually **reject** the promise in each case
else {
deferred.reject("Error: " + str + "not recognized.");
}
var promise = deferred.promise; // set up access to this eventual (promised) result
return promise; // return the promise
}
Elsewhere in our code, we can run the function and attach next steps for successful resolution or for rejection.
task("dude") // returns a promise
.then(success, error);
function success(resolveReturnValue){
console.log('resolved!', resolveReturnValue);
}
function error(rejectReturnValue){
console.log('rejected!', rejectReturnValue);
}
Promises can also be chained:
task("dude") // returns a promise
.then(task) // returns a promise
.then(success, error);
- What would you see logged in the console from the first example above?
click for answer
``` "dude" "resolved! dude" ```- What would you see logged in the console from the second example?
click for answer
``` "dude" "dude" "resolved! dude" ```-
Fork and clone this repo.
-
Play around with the simple Angular app in the sample-code directory. Remember to check your console output. Investigate how the
addOne
function is working. Try calling it a few more times. -
As you start to feel comfortable with
addOne
, implement asquare
function in this controller that can be used in the same way.
- What is a promise?
- What does it mean for a promise to be pending, fulfilled, resolved, and rejected?
- How does promise "chaining" work?
- Draw the lifecycle of a promise.