Promise Chaining

Objectives

  • Describe the disadvantages of using nested callbacks

  • Return a promise from a .then callback function

  • Use a promise to make asynchronous code seem sequential

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

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