Promises From The Ground Up
Building deep intuition for async JavaScript
There are a lot of speed bumps and potholes on the road to JavaScript proficiency. One of the biggest and most daunting is Promises.
In order to understand Promises, we need a surprisingly deep understanding of how JavaScript works and what its limitations are. Without that context, Promises won't really make much sense.
Why This Matters
Why would they design it this way??
Suppose we wanted to build a Happy New Year! countdown. If JavaScript was like most other programming languages, we could solve the problem like this:
class="text-pink-400">function newYearsCountdown() {
print(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">3");
sleep(class="text-amber-400">1000);
print(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">2");
sleep(class="text-amber-400">1000);
print(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">1");
sleep(class="text-amber-400">1000);
print(class="text-emerald-class="text-amber-400">400">"Happy New Year! 🎉");
}Unfortunately, there is no sleep function in JavaScript, because it's a single-threaded language. A "thread" is a long-running process that executes code. JavaScript only has one thread, and so it can only do one thing at a time.
Callbacks
The main tool in our toolbox for solving these sorts of problems is setTimeout. It accepts a chunk of work to do and the amount of time to wait for.
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">3…");
setTimeout(() => {
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">2…");
setTimeout(() => {
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">1…");
setTimeout(() => {
console.log(class="text-emerald-class="text-amber-400">400">"Happy New Year!!");
}, class="text-amber-400">1000);
}, class="text-amber-400">1000);
}, class="text-amber-400">1000);Callback Hell
Introducing Promises
Instead of nesting, what if we could chain them together? This is the core idea behind Promises. A Promise is a special construct, added to JavaScript in 2015.
class="text-pink-400">function wait(duration) {
class="text-pink-400">return new Promise((resolve) => {
setTimeout(resolve, duration);
});
}
wait(class="text-amber-400">1000)
.then(() => {
console.log(class="text-emerald-class="text-amber-400">400">'class="text-amber-400">2');
class="text-pink-400">return wait(class="text-amber-400">1000);
})
.then(() => {
console.log(class="text-emerald-class="text-amber-400">400">'class="text-amber-400">1');
class="text-pink-400">return wait(class="text-amber-400">1000);
})
.then(() => {
console.log(class="text-emerald-class="text-amber-400">400">'Happy New Year!!');
});Async / Await
One of the really great parts of modern JavaScript is the async/await syntax. Using this syntax, we can get pretty darn close to our ideal countdown structure:
class="text-pink-400">async class="text-pink-400">function countdown() {
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">5…");
class="text-pink-400">await wait(class="text-amber-400">1000);
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">4…");
class="text-pink-400">await wait(class="text-amber-400">1000);
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">3…");
class="text-pink-400">await wait(class="text-amber-400">1000);
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">2…");
class="text-pink-400">await wait(class="text-amber-400">1000);
console.log(class="text-emerald-class="text-amber-400">400">"class="text-amber-400">1…");
class="text-pink-400">await wait(class="text-amber-400">1000);
console.log(class="text-emerald-class="text-amber-400">400">"Happy New Year!");
}