Welcome back!

Promises

Redi School Munich - Spring 2021

Functions are variables!

Functions are variables!


              function foo() {
                // do something magic
              }

              console.log(typeof foo); // ==> "function"
            

Functions are variables!


              function foo(message) {
                alert(message);
              }

              const bar = foo;

              bar("It works!");
            

Types of functions

Types of functions

  • Named functions
    
                      function foo() { /* ... */ }
                    
  • Anonymous functions
    
                      function () { /* ... */ }
                    
  • Arrow functions
    
                      () => { /* ... */ }
                    
    These are anonymous by default.

Arrow functions

Syntax:


              const fn1 = (arg1, arg2) => {
                alert("Inside fn1");
                return arg1 + arg2;
              };
            
If the function consists just of a return statement, it can be shortened:

                const fn1 = (arg1, arg2) => {
                  return arg1 + arg2;
                };

                // These two are equivalent

                const fn2 = (arg1, arg2) => arg1 + arg2;
              

setTimeout

MDN: setTimeout

setTimeout

executes a function after a given number of milliseconds.


              function saySomething() {
                alert("Hello world");
              }

              setTimeout(saySomething, 1000); // ==> Shows "Hello world" after a second
            

Note that the function isn't called here; instead passed on just like a variable.


                // Do not:
                setTimeout(saySomething(), 1000);
              

Other examples where callback functions are needed

Sorting arrays by custom metrics


              const words = ["street", "cat", "animals", "tree"];

              // sorting by word length
              words.sort((a, b) => a.length - b.length);

              console.log(words); // ==> ["cat", "tree", "street", "animals"];
            

Other examples where callback functions are needed

Filtering arrays by custom metrics


              const mountains = ["Wallberg", "Seekarspitz", "Bärenkopf", "Setzberg"];

              // filtering all mountains that end in "berg"
              console.log(
                mountains.filter(a => a.endsWith("berg")) // ==> ["Wallberg", "Setzberg"];
              );
            

Writing our own functions that take callback arguments

An example


              function repeat(times, fn) {
                if (times <= 0) {
                  return;
                }

                for (let i = 0; i < times; i++) {
                  fn(); // Calling the passed function
                }
              }
            

Can you guess what this function does?

Promises

MDN: Using promises

An object representing an asynchronous process and the eventual completion or failure of such.

States of a promise

A Promise has three states:

  • After creation, it has the pending state.
  • On success, it changes to the state fulfilled.
  • On failure, it changes to the state rejected.

Using promises

Most promises we use on a daily basis are pre-made ones.


              functionThatReturnsPromise()
                .then(/* function to call when fulfilled */)
                .catch(/* function to call when rejected */);
            

Promise chaining

The result of each then and catch call is another Promise.


              functionThatReturnsPromise1()
                .then((returnValueOfPromise1) => functionThatReturnsPromise2())
                .then((returnValueOfPromise2) => functionThatReturnsPromise3());
            

Let's revisit fetch


              fetch("https://jsonplaceholder.typicode.com/posts/1")
                .then(response => response.json())
                .then(data => {
                  alert(data["body"]);
                });
            
  • fetch("...") returns a Promise
  • response.json() returns another Promise
  • We listen to the promise returned by response.json() in the last then call.

Error handling with fetch

If we catch errors at the end of the chain, we catch all errors that occur during any point of the chain.


              fetch("https://jsonplaceholder.typicode.com/posts/1")
                .then(response => response.json())
                .then(data => {
                  alert(data["body"]);
                })
                .catch(() => {
                  console.log("Something went wrong.");
                });
            

Error handling with fetch

We can also catch errors between individual steps:


              fetch("https://jsonplaceholder.typicode.com/posts/1")
                .catch(() => {
                  console.log("Could not fetch resource");
                })
                .then(response => response.json())
                .catch(() => {
                  console.log("Resource is not in json format");
                })
                .then(data => {
                  alert(data["body"]);
                });
            

Constructing Promises

Syntax of the Promise constructor:


              new Promise((resolve, reject) => {
                // Promise logic comes here.
              });
            
  • resolve and reject are functions.
  • Calling resolve() fulfils the promise.
  • Calling reject() rejects the promise.
  • Any argument passed into these methods will be available by the promise handler.

An example


              function wait(milliseconds) {
                return new Promise((resolve, reject) => {
                  setTimeout(resolve, milliseconds);
                });
              }

              wait(2000).then(() => {
                console.log('2 seconds passed');
              });
            

Promise composition

In addition to creating promises manually, we can compose multiple existing promises together into a single one

  • Promises.all takes an array of Promises and resolves, once all others resolve.
  • Promises.race takes an array of Promises and resolves, once any other resolves.

Example


              const promise1 = fetch("https://demo.url/resource1");
              const promise2 = fetch("https://demo.url/resource2");
              const promise3 = fetch("https://demo.url/resource3");

              const composition = Promises.all([promise1, promise2, promise3]);

              composition.then(() => {
                console.log("All resources finished loading");
              })
            

async and await

  • Promise chains can be turned more readable with the keywords async and await
  • Functions marked with the async keyword automatically wrap their return value in a promise.
  • await waits for the completion for a promise before moving on.

Example with fetch


              function fetchResource() {
                return fetch("https://demo.url/resource")
                  .then(result => result.json())
                  .then(data => doSomethingWith(data));
              }

              fetchResource()
                .then(returnValue => { console.log(returnValue) });
            

can be rewritten as


              async function fetchResource() {
                const result = await fetch("https://demo.url/resource");
                const data = await result.json();
                return doSomethingWith(data);
              }

              fetchResource()
                .then(returnValue => { console.log(returnValue) });