Promise practice
Background
Promises are an ES2015 standard, and are available natively in most modern browsers. Sadly, but as is typical in JavaScript world, there are several competing projects that implemented Promises based on various drafts of the Promise spec, and well before Promises were available natively. The enthusiastic developers of these projects also added their own features (like Deferreds, progress, state information, &c.) This adds a lot of noise and makes it harder for JS newcomers and old pros alike to grasp the core concepts of what problem Promises actually solve.
Different implementations you might use are:
- Bluebird
- Q
- Angular's $q (based on Q)
- es6-promise polyfill
Of these, the last strives to adhere to the spec. The others have a variety of other features.
Angular's $q service
When using Angular, always use $q. Don't use native Promise
(or a polyfill).
This is because $q is wired into the $digest cycle: it calls $scope.$apply
for you when the promise resolves so your UI stays in sync.
What problem do Promises solve?
- Promises represent an eventual value.
- Promises encapsulate the asynchronousness of code, letting you write it as if it were synchronous.
- Promises get you out of callback hell
What does "asynchronous code written synchronously" look like?
Traditionally callbacks are passed as an argument to a function, and that function calls them when appropriate. With Promises, the continuation is instead given to the return value of the function. Compare:
Accepting a callback:
function getAsyncVal(callback) {
setTimeout(() => callback('foo'), 200);
}
getAsyncVal(val => {
console.log(val)
});
Returning a Promise:
function getAsyncValPromise() {
return new Promise(resolve => {
setTimeout(() => resolve('foo'), 200);
});
}
getAsyncValPromise()
.then(val => {
console.log(val)
});
See examples.js
for more side-by-side comparisons of callbacks and Promises.
A Promising Future
Promises are getting even more play in ES7 with async
functions! Check out
this sweet syntax:
async function loadPage() {
try {
content = await getJSON('/api/main/content');
showPage(content);
} catch (e) {
showError('Arrrggh everything is broken');
logError(e);
}
}
Exercise
We're going to implement a Promise
class that adheres to the spec. The tests
in test/promise.spec.js
are written, you just need to implement promise.js
.
(Oh, hey TDD.)
Why?
Promises are mind-bending when you first see them. (And sometimes later, too.) The thing they encapsulate is subtle, but powerful when you use it right.
Not only that, but one of the great use cases of Promises is a Promise-based
API. And it just so happens that the Promise
class itself implements a
Promise-based API!
How?
$ npm install && npm start
to start the test watcher. Then open your editor and implement all the
things in promise.js
to make the tests pass.
Note: the promise.js
template is written as an ES6 class
for simplicity.
Too long; didn't code?
Checkout the solution
branch to see a sample implementation, or solution2
for a slightly different one.