0

I have been studying and trying to understand Asynchronous javascript. After much reading and a suggestion to use the bluebird library because I'm using IE11, I tried it on a simple example I created but it isn't working as expected. I added a settimeout in each function to simulate asynchronicity. The goal is to populate the arrays so I can console.log() the array values but it was to no avail. In my promise.all, I call createNavigation() which suggests that all my arrays are populated but it isn't. Also, numbers are being returned for the results in promise.all.
What am I doing wrong or misunderstanding? Why are my arrays being logged to the console as blanks?

var cacheNavData = [];
var cacheSubNavData = [];
var cacheMegaMenuData = [];
var cacheCategoryMenuData = [];

getNavData();
getSubNavData();
getMegaMenuData();
getCategoryMenuData();

var promises = [
  getNavData(),getSubNavData(),getMegaMenuData(),getCategoryMenuData()
]

Promise.all(promises)
 .then(function(results){
 console.log(results)
 createNavigation()
})
function getNavData(){
   return setTimeout(function(){ 
    cacheNavData[0] = "Soup";
    cacheNavData[1] = "Sandwich";
    cacheNavData[2] = "Rice";  
   }, 3000);
 }

 function getSubNavData(){
   return setTimeout(function(){ 
    cacheSubNavData[0] = "Apple";
    cacheSubNavData[1] = "Beans";
    cacheSubNavData[2] = "Carrot";    
  }, 3000);

 }

 function getMegaMenuData(){
    return setTimeout(function(){ 
    cacheMegaMenuData[0] = "Donkey";
    cacheMegaMenuData[1] = "Eagle";
    cacheMegaMenuData[2] = "Frog";
 }, 3000);
}

function getCategoryMenuData(){
   return setTimeout(function(){ 
    cacheCategoryMenuData[0] = "Grapes";
    cacheCategoryMenuData[1] = "Hand";
    cacheCategoryMenuData[2] = "Igloo";    
   }, 3000);
 }

 function createNavigation(){
   console.log("All arrays have been populated.  Let's build the navigation.")

 }

 console.log(cacheNavData);
 console.log(cacheSubNavData);
 console.log(cacheMegaMenuData);
 console.log(cacheCategoryMenuData);

codepen

Liam
  • 27,717
  • 28
  • 128
  • 190
OLA
  • 861
  • 2
  • 11
  • 23
  • 1
    setTimeout does not return any promises hence `promise.all` has no effect. You need to create a `Promise` (or a `Bluebird` which is a special promise) and return it by your functions. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise – messerbill Feb 07 '20 at 11:58
  • So do I wrap settimeout in a resolve? – OLA Feb 07 '20 at 12:00
  • the answer below demonstrates how to do that – messerbill Feb 07 '20 at 12:02

1 Answers1

3

That is because in your functions you are returning the ID of the timer, instead of returning a promise that is resolved when the timer runs out. Therefore, the execution is almost immediate. What you want is to ensure that you are returning promises instead of timer IDs in your methods. Here is an example:

function getNavData(){
   return new Promise(function(resolve) {
     setTimeout(function(){ 
      cacheNavData[0] = "Soup";
      cacheNavData[1] = "Sandwich";
      cacheNavData[2] = "Rice";

      resolve();
     }, 3000);
   });
 }

If you want the promise to return data, then instead of using resolve(), you pass the data you want to resolve with as the first argument, e.g. resolve(cacheNavData).

You should also not be console logging at the end of your file, since those arrays will be empty. If you want to access them, you should read them from results instead, if you are resolving with a payload. You can always use ES6 array spread to unpack the results:

Promise.all(promises)
 .then(function(results){
  console.log(results);
  const [navData, subNavData, megaMenuData, categoryMenuData] = results;
  console.log(navData, subNavData, megaMenuData, categoryMenuData);
  createNavigation();
});

This also means that you don't need to declare the global arrays for all your data: your individual functions have the responsibility of passing the populated arrays back to Promise.all.

See proof-of-concept example:

var promises = [
  getNavData(),getSubNavData(),getMegaMenuData(),getCategoryMenuData()
]

Promise.all(promises)
 .then(function(results){
 console.log(results);
 const [navData, subNavData, megaMenuData, categoryMenuData] = results;
 console.log(navData, subNavData, megaMenuData, categoryMenuData);
 createNavigation();
})
function getNavData(){
   return new Promise(function(resolve) {
     setTimeout(function(){ 
      var cacheNavData = [];
      cacheNavData[0] = "Soup";
      cacheNavData[1] = "Sandwich";
      cacheNavData[2] = "Rice";
      
      resolve(cacheNavData);
     }, 3000);
   });
 }

 function getSubNavData(){
   return new Promise(function(resolve) {
     setTimeout(function(){
      var cacheSubNavData = [];
      cacheSubNavData[0] = "Apple";
      cacheSubNavData[1] = "Beans";
      cacheSubNavData[2] = "Carrot";
      
      resolve(cacheSubNavData);
    }, 3000);
   });
 }

 function getMegaMenuData(){
  return new Promise(function(resolve) {
    setTimeout(function(){ 
      var cacheMegaMenuData = [];
      cacheMegaMenuData[0] = "Donkey";
      cacheMegaMenuData[1] = "Eagle";
      cacheMegaMenuData[2] = "Frog";

      resolve(cacheMegaMenuData);
    }, 3000);
  });
}

function getCategoryMenuData(){
   return new Promise(function(resolve) {
     setTimeout(function(){ 
      var cacheCategoryMenuData = [];
      cacheCategoryMenuData[0] = "Grapes";
      cacheCategoryMenuData[1] = "Hand";
      cacheCategoryMenuData[2] = "Igloo"; 
      
      resolve(cacheCategoryMenuData);
     }, 3000);
    });
 }

 function createNavigation(){
   console.log("All arrays have been populated.  Let's build the navigation.")

 }
Terry
  • 63,248
  • 15
  • 96
  • 118
  • Thanks for the proof of concept @Terry. Why does my console.log() still show empty arrays? – OLA Feb 07 '20 at 12:13
  • @OLA The last 4 lines of the code are called before the promises are resolved, so all the arrays are empty at that moment. – Terry Feb 07 '20 at 12:20