Promise

Basics

Objectives

  • Define a promise

  • Add a .then callback to a promise

  • Add a .catch callback to a promise

  • Wrap a setTimeout call in a promise

Nested Async Callbacks

 var counter = 0;
 setTimeout(function() {
   counter++;
   console.log("Counter:", counter);
   setTimeout(function() {
     counter++;
     console.log("Counter:", counter);
     setTimeout(function() {
       counter++;
       console.log("Counter:", counter);
     }, 3000);
   }, 2000);
 }, 1000);

Console:

Counter: 1

Counter: 2

Counter: 3

Disadvantages of Nested Callbacks

  • The code is hard to read

  • Logic is difficult to reason about

  • The code is not modular

Promise: Conceptually

A promise is an object that represents a task that will be completed in the future

 

Analogy: Taking an number at a government office before you can get helped.  The piece of paper you get is like your promise.  The help you get at the counter is like the invocation of your callback.

Creating a Promise

 var p1 = new Promise(function(resolve, reject) {
   resolve([1,2,3,4]);
 });

 p1.then(function(arr) {
   console.log("Promise p1 resolved with data:", arr);
 });

Console:

Promise p1 resolved with data: [1,2,3,4]

Promise: Handling Errors

 var p1 = new Promise(function(resolve, reject) {
   reject("ERROR");
 });

 p1.then(function(data) {
   console.log("Promise p1 resolved with data:", data);
 }).catch(function(data) {
   console.log("Promise p1 was rejected with data:", data);
 });

Console:

Promise p1 was rejected with data: ERROR

Promise:

With Randomly Occurring Errors

 var p1 = new Promise(function(resolve, reject) {
   var num = Math.random();
   if (num < 0.5) {
     resolve(num);
   } else {
     reject(num);
   }
 });

 p1.then(function(result) {
   console.log("Success:", result);
 }).catch(function(error) {
   console.log("Error:", error);
 });

Wrap setTimeout With Promise

 var promise = new Promise(function(resolve, reject) {
   setTimeout(function() {
     var randomInt = Math.floor(Math.random() * 10);
     resolve(randomInt);
   }, 4000);
 });

 promise.then(function(data) {
   console.log("Random int passed to resolve:",
               data);
 });

Returning a Promise:

Promise Chaining

var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    randomInt = Math.floor(Math.random() * 10);
    resolve(randomInt);
  }, 500);
});

promise.then(function(data) {
  console.log("Random int passed to resolve:",
               data);
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(Math.floor(Math.random() * 10));
    }, 3000);
  });
}).then(function(data) {
  console.log("Second random int passed to resolve:",
               data);
});

Promise Chaining:

Returning Data

 var promise = new Promise(function(resolve, reject) {
   resolve(5);
 });

 promise.then(function(data) {
   return data * 2;
 }).then(function(data) {
   return data + 20;
 }).then(function(data) {
   console.log(data);
 });

Console:

30

Nested Callbacks:

To Be Refactored

 

 var counter = 0;
 setTimeout(function() {
   counter++;
   console.log("Counter:", counter);
   setTimeout(function() {
     counter++;
     console.log("Counter:", counter);
     setTimeout(function() {
       counter++;
       console.log("Counter:", counter);
     }, 3000);
   }, 2000);
 }, 1000);

Console:

Counter: 1

Counter: 2

Counter: 3

Step 1:

Create a Function Declaration

 

 var counter = 0;
 function incCounter() {
   counter++;
   console.log("Counter:", counter);
 }

Step 2:

Create a runLater Function

 var counter = 0;
 function incCounter() {
   counter++;
   console.log("Counter:", counter);
 }

 function runLater(callback, timeInMs) {
   var p = new Promise(function(resolve, reject) {
     setTimeout(function() {
       var res = callback();
       resolve(res);
     }, timeInMs);
   });
   return p;
 }

Step 3: Chain Promises

 var counter = 0;
 function incCounter() {
   counter++;
   console.log("Counter:", counter);
 }

 function runLater(callback, timeInMs) {
   var p = new Promise(function(resolve, reject) {
     setTimeout(function() {
       var res = callback();
       resolve(res);
     }, timeInMs);
   });
   return p;
 }

 runLater(incCounter, 1000).then(function() {
   return runLater(incCounter, 2000);
 }).then(function() {
   return runLater(incCounter, 3000);
 }).then(function() {
   // final .then not necessary
 });

Promise Refactor: Side By Side

 var counter = 0;
 function incCounter() {
   counter++;
   console.log("Counter:", counter);
 }

 function runLater(callback, timeInMs) {
   var p = new Promise(function(resolve, reject) {
     setTimeout(function() {
       var res = callback();
       resolve(res);
     }, timeInMs);
   });
   return p;
 }

 runLater(incCounter, 1000).then(function() {
   return runLater(incCounter, 2000);
 }).then(function() {
   return runLater(incCounter, 3000);
 });
 var counter = 0;
 setTimeout(function() {
   counter++;
   console.log("Counter:", counter);
   setTimeout(function() {
     counter++;
     console.log("Counter:", counter);
     setTimeout(function() {
       counter++;
       console.log("Counter:", counter);
     }, 3000);
   }, 2000);
 }, 1000);

It is useful to understand how promises work (resolve, reject), but in practice you will often use promises that are returned to you

Promises In Practice