Promises I

In this tutorial you'll verify your knowledge of Promise API, behaviors and patterns. You'll deal with multiple paradigms: Sync-and-Async, OOP-and-FP; sometimes at the same time. You'll practice important aspects like Scoping, Laziness, Composition, as well as commonly used patterns like promisification and de-promisification. After this tutorial you'll be able to twist and bend any promise-based code like a true magician!

Covered Topics

Class vs Instance
Sync vs Async
Evaluation order
Error handling
Callbacks vs Promises



Recommended resources


From the author

Teaching programming, there's always a dilemma between explaining how it is and how it should be. There's rarely a place and time to cover both so you have to choose. This dilemma is especially painful when it comes to Promises A+ (the version adopted by NodeJS and browsers). They are pretty complex to waste all our explanatory resources and, at the same time, so poorly designed that we can't pretend that "everything's fine, just remember this and that".

Should we follow a bad specification or teach you some better async patterns departing from the standard? Tough choice, so it's fortunate that we found some kind of compromise. We well explain and challenge promises from a simplistic (and more sound) point of view, ignoring some nasty corner cases. To give you an example, A+ promises introduce state and fate concepts:

Promises have two possible mutually exclusive fates: resolved, and unresolved.

  • A promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected.
  • A promise is unresolved if it is not resolved, i.e. if trying to resolve or reject it will have an impact on the promise. A promise whose fate is resolved can be in any of the three states:
  • Fulfilled, if it has been resolved to a non-promise value, or resolved to a thenable which will call any passed fulfillment handler back as soon as possible, or resolved to another promise that is fulfilled.
  • Rejected, if it has been rejected directly, or resolved to a thenable which will call any passed rejection handler back as soon as possible, or resolved to another promise that is rejected.
  • Pending, if it has been resolved to a thenable which will call neither handler back as soon as possible, or resolved to another promise that is pending.

You'll have a lot of head-scratching moment, when you realize that resolved promise can be pending and trying to relate all that to resolve function... Than, you'll get used to it and start to attack people questioning those weird names as noobs. In a few years you'll realize that your initial impression was correct and the original design is really a bullshit caused, to a large extent, by auto-flattening of promises and merging map and flatMap into a single then.

Remove that and you'll have three predictable states: "pending", "resolved", and "rejected" with absolutely no need for "fates", "settlements" and stuff like that. Since that moment you're ashamed and hopefully stop demonizing people for "questioning the authority".

It's not a hindsight, many people were vocal about the design issues of A+, but incompetence prevailed. And it's just a single example to unfold our claims a bit. We could continue but this is not an article. Check the link list below for some additional details.

In the upcoming Advanced tutorial we'll redesign promises from scratch, so we'll have an opportunity to look at sources and discuss engineering flaws of A+ in details. Meanwhile, we just ask you to follow the first part and judge for yourself. It's really a challenging goal to skim over controversial moments, to keep explanations simple, and to actually teach something useful and portable at the same time. Yet we believe the goal is achieved in this tutorial.

63 exercises for free