# Promises

  • Before discussing promises and why we need it, first take a look at this simple four-step calculation:
    • start with x = 5
    • step 1: multiply x by 2 (x = 10)
    • step 2: add 6 to x (x = 16)
    • step 3: subtract x by 1 (x = 15)
    • step 4: output x to the pre tag in the DOM (<pre>x = 15</pre>)
  • Open es6/promises/calculator_synchronous.html and es6/promises/calculator_synchronous.js
  • Let's do the same calculation, but this time:
    • stap 1 has a delay of two seconds
    • stap 2 has a delay of one second
  • Open es6/promises/calculator_asynchronous.html and es6/promises/calculator_asynchronous.js

# Synchronous vs Asynchronous functions

  • To explain the behavior of the previous example, we have to know the difference between synchronous and asynchronous functions in JavaScript
  • Javascript is a single threaded language:
    • This means it has only one call stack
    • A line of code must finish executing before it's moving to the next line of code
    • This is called synchronous
  • Most of the functions we already used are synchronous (e.g. console.log(), forEach(), while(), ...)
  • Some function in JavaScript can't give us a result immediately, gives us a result in time (e.g. fetch(), setTimeout(), setInterval(), DOM events, ...)
  • These functions are called asynchronous function and are treated differently
    • When a synchronous function is recognizes, this functions is taken out of the main thread, so the execution don't block, and the next line of code is executed
    • When the synchronous function is resolved, and when the main thread is finished, the result is sent back to the main thread
    • Asynchronous function are non-Blocking
  • Now the result of our previous example make sense:

# Callback (hell)

  • What we really want is that the second setTimeout() function will be executed after the fitst setTimeout() and the last steps must be executed after the second setTimeout() function
  • With the knowledge we have so far, there is only one way to resolve our problem: nested callback function
  • Open es6/promises/calculator_callback.html and es6/promises/calculator_callback.js
  • As you can see, we have a lot of nested functions inside each other
  • This code is hard to read and hard to maintain, so they called it callback hell or the pyramid of Doom

# Convert nested callbacks to promises

  • ES6 introduced a nicer, and a more elegant way to do async operations without the need of nested callbacks, called promises
  • Some features in JavaScript are already build upon promises (e.g. the fetch API) but sometimes you have to create your own promise like in previous example
  • A promise is just an object that can created with new Promise() and has three states:
    • pending: response is not ready yet, please wait....
    • fulfilled: response is ready, and the data can be used now (data is resolved)
    • rejected: an error occurred, and there is no data (data is rejected)
  • As we already know from fetch() chapter, the promise can be chained with one or more then() handlers to do something with the resolved data and one catch() handler that catches the (reject) error
  • This is a basic skeleton for a promise that don't except any parameters:
const promise = new Promise((resolve, reject) => {
    // do something that gives some data back after a certain time
    if (/* everything turned out fine -> resolve the data */) {
        resolve(data);
    } else {
        reject(new Error('Oops, something went wrong'));
    }
});

promise // without parentheses!
    .then(data => console.log(data)) // the data from the 'resolved' promise is available and can be used now
    .catch(error => console.error(error));
1
2
3
4
5
6
7
8
9
10
11
12
  • This is a basic skeleton for a promise that except one or more parameters:
const promise = (param1, param2) => {
    return new Promise((resolve, reject) => {
        // do something that gives some data back after a certain time
        if (/* everything turned out fine -> resolve the data */) {
            resolve(data);
        } else {
            reject(new Error('Oops, something went wrong'));
        }
    });
}

promise(param1, param2) // with parentheses of course
    .then(() => console.log('resolved')) // the data from the 'resolved' promise is available
    .catch(error => console.error(error));
1
2
3
4
5
6
7
8
9
10
11
12
13
14

REMARK

If you only have to resolve a promise without sending data back, you can use resolve() instead of resolve(data)

  • There are two posible solutions to transform our calculation example from nested callbacks to promises:
    • Create two promises (one for every setTimeout() function) and return the result of the operation via resolve(...) inside the promise
    • Create only one promise (just a delay promise) and do the operations that are now inside the setTimeout() function in a then() handler after the timer is resolved
  • Let's refactor the calculator with one delay promise that we can use multiple times

# Refactor the delay promise

# Run promises in parallel

  • Open es6/promises/parallel.html and es6/promises/parallel.js
  • Use the all() handler to run multiple promises at the same time (in parallel) and process the data after all promises are resolved
    • the parameter of the all() handler contains an array of promises
    • the resolved data of all promises (also an array) is available in the then() handler after the slowest promise is resolved
    • the order the resolved data-array is the same order as the array in the all() handler

# Examples

# Karaoke

  • Open es6/promises/karaoke.html and es6/promises/karaoke.js

# Random words (advanced)

  • Open es6/promises/random_words.html and es6/promises/random_words.js
  • Suppose we want to fetch x random words from this API (opens new window)
  • The result is an array of one item (opens new window) whose word and description we place in a definition list
  • There is no need to make three API calls one after the other (in series), but we will make the three API calls simultaneously (in parallel)
Last Updated: 4/12/2021, 7:55:49 AM