This is a copy of Mariko Kosaka (@kosamari) notes, just to keep it available on the web.
Two weeks ago, I was in a conversation about how I might implement a feature in JavaScript. It needed to be asynchronous to access external data, I said « well, let’s use fetch()
…so in code… umm… » while I paused to remember fetch API, the person I was talking to said, « It returns a promise ». My brain froze, and I said: « I honestly don’t know what you mean… »
I’ve written promise based code plenty of times, but somehow things didn’t connect in my brain this time. I realized I actually don’t « get » promise after all.
I can not tell you how hard it is to explain this sentence – « It returns a Promise »
but probably because I really don’t understand Promise.— Mariko Kosaka (@kosamari) January 13, 2017
If you know me from twitter, I’m a visual learner who draws code concepts as physical metaphor. It is how I cope with a double layer of abstraction (programming language & English as a second language). So naturally, I had to draw it this time as well.
Here is a piece of code we are going to look at in this story.
// asynchronous operation
function cookBurger (type) { ... }
// regular operation
function makeMilkshake (type) { ... }
// order function which returns promise
function order (type) {
return new Promise(function(resolve, reject) {
var burger = cookBurger(type)
burger.ready = function (err, burger) {
if (err) {
return reject(Error('Error while cooking'))
}
return resolve(burger)
}
})
}
order('JakeBurger')
.then( burger => {
const milkshake = makeMilkshake('vanilla')
return { burger: burger, shake: milkshake }
})
.then( foodItems => {
console.log('BURGER PARTY !', foodItems)
})
.catch( err => {
console.log(err)
})
Let’s have a burger party
Welcome to the Promise Square Park, home of a burger joint JakeShack. The burger at JakeShack is very popular but it has a limited number of cash registers to take orders, so the line is always long. However, the back kitchen is well staffed to take multiple orders at a time. In case you are not familiar, Madison Square Park and ShakeShack is a thing in New York City. It’s really good, but the line is always long.
Promisify the action
In order to take orders as quickly as possible, JakeShack uses a buzzer system. When a customer makes an order at a cash register, register staff hand you a tray and a buzzer in exchange of payment.
The tray is a promise from JakeShack that they will place its delicious burger on here once it’s ready, and a buzzer is an indicator of order status. When buzzer is not beeping, it means the order is pending – the kitchen crew is busy at work processing your order. When buzzer turns red and beeps, it means the order is settled.
A bit of fine nuance here on settled. It is not equal to « ready ». It means the order has been processed in the kitchen and calling the customer to take action on it. You (a customer) probably want to pick up your order at the counter, but in some case, you might just walk away. It’s up to you.
Let’s look at what we have so far in code. When you call the order
function, it « returns a promise » (giving you a tray with a buzzer). A return value (a burger) should appear on the tray once the promise is fulfilled and callback function called. More on that in next section!
Add Promise handlers
Looks like the buzzer is beeping, let’s go to the counter and claim the order. There are two scenarios you can expect at this stage.
1. Order fulfilled
Hooray! your burger is ready, the kitchen staff hands you a freshly made burger. The promise of a good burger was fulfilled!
2. Order rejected
Looks like the kitchen ran out of burger patties, the promise of a burger was rejected. Make sure you get a refund for that!
Here is how you might prepare for these two situations in code.
.then()
takes another function as the second argument which can also be used as a reject handler. For the sake of simplicity, I only use .catch()
for reject in this article. If you want to know more about the difference, you might want to check out this article.
Chain the Promise
Let’s say your order was fulfilled, but you realized in order to have an ultimate burger party, you also need a milkshake… so you walk up to the C-line (a special line for drinks, real thing at ShakeShack for optimal crowd control). When you order a milkshake at the counter, a cashier gives you another tray and an another buzzer. Since milkshake is super quick to make, the cashier will hand you milkshake as well. No need to wait for the buzzer to beep (it’s already beeping !).
Let’s look at how it works in code. Chaining promise is as easy as adding another .then()
in your code. Return from .then()
is always a promise. Just remember each .then()
returns you a tray and a buzzer, and an actual return value is passed as an argument to the callback.
Now that you have burger and milkshake, you are ready to BURGER PARTY 🎉
More party tricks!
There are few more methods in promise that let you perform cool tricks.
Promise.all()
creates a promise that takes an array of promises (items). This promise fulfills when all of its items (each one is a promise) are fulfilled. Imagine you ordered 5 different burgers for your group but want to minimize a trip to the counter to only once when all 5 orders are ready. Promise.all()
is a good solution for this.
Promise.race()
is similar to Promise.all()
. But it is fulfilled or rejected as soon as one item fulfills or rejects. This can be used to simulate try and grab. If you are super hungry, you might order a burger, a cheeseburger, and a hot dog at the same time, only to pick up whatever the 1st order came out of the kitchen. (Note, in this analogy if the kitchen is out of burger patty and rejects the burger promise first, then the entire race promise will turn to rejected state.)
There are more details to cover in promise. Here is a further reading suggestion.
- promise-cookbook written in English (Chinese version available)
- JavaScript Promises: an Introduction written in English
- JavaScript Promiseの本 written in Japanese (Chinese and Korean version available)
Thanks to Jake Archibald and Nolan Lawson for proofreading this post and suggesting changes! and Chris Wheatley, Juan Lopez, and Nicolas Dermine for fixing typo.
This post is published under CC BY-NC-SA 4.0 license. Feel free to translate & publish under the license.
Please let me know if you published a translation!