Promises

    Promises are an abstraction for asynchronous control flow — they're a proxy object for a value that may not exist yet. A Promise is always in 1 of 3 states internally:

    • Pending
    • Resolved
    • Rejected

    A resolved or rejected promise optionally contains a value.

    Every promise starts in the pending state, and can only transition once (i.e. a resolved promise never becomes rejected later).

    Promises are similar to futures and deferred values in other languages (https://en.wikipedia.org/wiki/Futures_and_promises).

    Creating promises

    We can immediately wrap any value in a promise using the static Promise.resolved and Promise.rejected methods.

    More commonly, we use the constructor, which we can resolve or reject at any point in the future.

    Chaining promises

    We can use .then and .catch to chain operations together. These methods both return new promises.

    If we throw an error or return a rejected promise, all .then calls will be skipped until a .catch is found.

    By convention, a rejected promise should contain an error object rather than an arbitrary value. Throwing an error implicitly creates a rejected promise.

    Handling events

    So far, we've been using promises synchronously, but we typically use them asynchronously while working with the event loop.

    For example, we might wrap a setTimeout call in a promise.

    Anywhere we use a callback that will only be called once we should consider using a Promise instead.

    Multiple promises

    We can use promises to run multiple asynchronous tasks in parallel.

    Promise.all returns a promise that resolves if all promises in an array resolve, while Promise.race resolves if a single promise in the array to resolve.