Best way to manage async API calls with objects?

Hello,

I’ve had some issues with async functions in the past, and have had to get help on the forum, and in the end, I was instructed on how to use a Promise with API data that was being pushed into an array. Now, however, I’m working on a personal project that requires multiple API calls that push data into JSON objects, and so the things I had tried before that used to work for arrays, don’t work as nicely with JSON objects. What is the best approach to doing this? This app uses express, so basically my overarching problem is that I’m performing the res.json function before the JSON object is filled out with the data it needs.

Can you provide some example content to understand the scenario?

@bunnyvishal6

Sheet.getCells is a function within a Google Sheets API FYI, but it’s basically just a normal API call.

    var rowersOptions = {
      'min-row': 4,
      'max-row': maxRows, //Defined earlier
      'min-col': 2,
      'max-col': 2,
      'return-empty': true
    };
    sheet.getCells(rowersOptions, function(err, cells) {

      var CellData = {};
      //Get Rowers
      var rowers = [];
      for (var i = 0; i<cells.length; i++) {
        rowers.push(cells[i].value);
      }

    });


      var date = "6/18" //NEED TO CHANGE DATE TO REAL DATE








      //Function for finding data of Women's Sculls
      function getWomen (col) {
        var columnOptions = {
          'min-row': 4,
          'max-row': maxRows,
          'min-col': col,
          'max-col': col,
          'return-empty': true
        };
      sheet.getCells(columnOptions, function(err, cells) {
        for (var i = 0; i<cells.length; i++) {
          var name = rowers[i];
          if (cells[i].value === "1") {
            finalComingWomen[name] = "YES";
          } else if (cells[i].value === "0") {
            finalComingWomen[name] = "NO";
          } else {
            finalComingWomen[name] = "N/A";
          }
        }
      });
      }


      //Finding Data for Open Sculls
      function getOpen (col) {
        var columnOptions = {
          'min-row': 4,
          'max-row': maxRows,
          'min-col': col,
          'max-col': col,
          'return-empty': true
        };
      sheet.getCells(columnOptions, function(err, cells) {
        for (var i = 0; i<cells.length; i++) {
          var name = rowers[i];
          if (cells[i].value === "1") {
            finalComingOpen[name] = "YES";
          } else if (cells[i].value === "0") {
            finalComingOpen[name] = "NO";
          } else {
            finalComingOpen[name] = "N/A";
          }
        }
      });
      }

      //Finding Column Applicable for each data set, and then grabbing data from Google Sheets API with the column number
      var dateOptions = {
        'min-row': 2,
        'max-row': 2,
        'return-empty': true
      };
      sheet.getCells(dateOptions, function(err, cells) {
        for (var i = 0; i<cells.length; i++) {
          if (date == cells[i].value){
          getWomen(Number(cells[i].col));
          getOpen(Number(cells[i].col) + 1);
          break;
          }
        }
      });
      res.json({finalComingOpen, finalComingWomen]});

promisify + async

@njanne19 as functions invlove some async operation. You should use promises or you can use traditional callbacks.
Note that you are using for loops with async data. Then before completion of the async calls for loop ends and res.json will get called before the finalComingWomen and finalComingOpen get filled with required data.

I tried with callbacks if you like you can see this. And where did you declare “finalComingWomen” and “finalComingOpen” in your code? I assume that these were defined previously or these are global variables.

var rowersOptions = {
    'min-row': 4,
    'max-row': maxRows, //Defined earlier
    'min-col': 2,
    'max-col': 2,
    'return-empty': true
};
sheet.getCells(rowersOptions, function(err, cells) {

    var CellData = {};
    //Get Rowers
    var rowers = [];
    for (var i = 0; i < cells.length; i++) {
        rowers.push(cells[i].value);
        //the ieteration came to an end
        if (i == cells.length - 1) {
            var date = "6/18" //NEED TO CHANGE DATE TO REAL DATE

            //Function for finding data of Women's Sculls
            function getWomen(col, cb) {
                var columnOptions = {
                    'min-row': 4,
                    'max-row': maxRows,
                    'min-col': col,
                    'max-col': col,
                    'return-empty': true
                };
                sheet.getCells(columnOptions, function(err, cells) {
                    for (var i = 0; i < cells.length; i++) {
                        var name = rowers[i];
                        if (cells[i].value === "1") {
                            finalComingWomen[name] = "YES";
                        } else if (cells[i].value === "0") {
                            finalComingWomen[name] = "NO";
                        } else {
                            finalComingWomen[name] = "N/A";
                        }
                        //During the iteration when i == cells.length - 1 i is the maximum index in cells array. 
                        //Call the callback so that this function is done and got the required data
                        if (i == cells.length - 1) {
                            //call the next one
                            cb();
                        }
                    }
                });
            }


            //Finding Data for Open Sculls
            function getOpen(col, cb) {
                var columnOptions = {
                    'min-row': 4,
                    'max-row': maxRows,
                    'min-col': col,
                    'max-col': col,
                    'return-empty': true
                };
                sheet.getCells(columnOptions, function(err, cells) {
                    for (var i = 0; i < cells.length; i++) {
                        var name = rowers[i];
                        if (cells[i].value === "1") {
                            finalComingOpen[name] = "YES";
                        } else if (cells[i].value === "0") {
                            finalComingOpen[name] = "NO";
                        } else {
                            finalComingOpen[name] = "N/A";
                        }
                        //During the iteration when i == cells.length - 1 i is the maximum index in cells array. 
                        //Call the callback so that this function is done and got the required data
                        if (i == cells.length - 1) {
                            cb();
                        }
                    }
                });
            }

            //Finding Column Applicable for each data set, and then grabbing data from Google Sheets API with the column number
            var dateOptions = {
                'min-row': 2,
                'max-row': 2,
                'return-empty': true
            };
            sheet.getCells(dateOptions, function(err, cells) {
                var cellsCount = 0;

                function getData() {
                    if (date == cells[cellsCount].value) { //if the date is cells[cellsCount].value then we get required data
                        //call to getWomen
                        getWomen(Number(cells[cellsCount].col), function() {
                            //call to getOpen 
                            getOpen((Number(cells[cellsCount].col) + 1), function() {
                                res.json({ finalComingOpen, finalComingWomen });
                            });
                        });
                    } else { //else means date not equals to cells[cellsCount].value increase the cellsCount by one as if in for loop
                        cellsCount++;
                        if (cellsCount < cells.length) { //if the new cellsCount is less than cells.length then call this function again.
                            getData();
                        }
                    }
                }

            });
        }
    }

});

@bunnyvishal6

I tried doing something like this, but now it tells me that my variable rowers is not defined. However, it seems to be in the scope of the function that it comes up as undefined in.


    var rowersOptions = {
      'min-row': 4,
      'max-row': maxRows, //Defined earlier
      'min-col': 2,
      'max-col': 2,
      'return-empty': true
    };
    sheet.getCells(rowersOptions, function(err, cells) {

      var CellData = {};
      //Get Rowers
      var rowers = [];
      for (var i = 0; i<cells.length; i++) {
        rowers.push(cells[i].value);
      }

    });


      var date = "6/18" //NEED TO CHANGE DATE TO REAL DATE




      function getMyAsync (col) {
        return new Promise(function(resolve, reject) {
          var ticker = 0;
          var columnOptions1 = {
            'min-row': 4,
            'max-row': maxRows,
            'min-col': col,
            'max-col': col,
            'return-empty': true
          };
          var columnOptions2 = {
            'min-row': 4,
            'max-row': maxRows,
            'min-col': col+1,
            'max-col': col+1,
            'return-empty': true
          };
          sheet.getCells(columnOptions1, function(err, cells) {
            for (var i = 0; i<cells.length; i++) {
              var name = rowers[i];
              if (cells[i].value === "1") {
                finalComingWomen[name] = "YES";
              } else if (cells[i].value === "0") {
                finalComingWomen[name] = "NO";
              } else {
                finalComingWomen[name] = "N/A";
              }
              if (i == cells.length - 1) {
                ticker ++;
              }
            }
          });

          sheet.getCells(columnOptions2, function(err, cells) {
            for (var i = 0; i<cells.length; i++) {
              var name = rowers[i];
              if (cells[i].value === "1") {
                finalComingOpen[name] = "YES";
              } else if (cells[i].value === "0") {
                finalComingOpen[name] = "NO";
              } else {
                finalComingOpen[name] = "N/A";
              }
              if (i == cells.length - 1) {
                ticker ++;
              }
            }
          });

            if (ticker == 2) {
              resolve();
            }

        });
      }


      //Finding Column Applicable for each data set, and then grabbing data from Google Sheets API with the column number
      var dateOptions = {
        'min-row': 2,
        'max-row': 2,
        'return-empty': true
      };
      sheet.getCells(dateOptions, function(err, cells) {
        for (var i = 0; i<cells.length; i++) {
          if (date == cells[i].value){
          getMyAsync(Number(cells[i].col)).then(function() {
            res.json({finalComingOpen, finalComingWomen});
          });
          break;
          }
        }
      });

@njanne19 no rowers is not in scope of the functions that are using the rowers variable. Define all your async functions inside the function which declares rowers.

      var rowersOptions = {
       'min-row': 4,
       'max-row': maxRows, //Defined earlier
       'min-col': 2,
       'max-col': 2,
       'return-empty': true
   };
   sheet.getCells(rowersOptions, function(err, cells) {

       var CellData = {};
       //Get Rowers
       var rowers = [];
       for (var i = 0; i < cells.length; i++) {
           rowers.push(cells[i].value);
           //when iteration comes to an end
           if (i == cells.length - 1) {
               //define your async function here
               function getMyAsync(col) {
                   return new Promise(function(resolve, reject) {
                       var ticker = 0;
                       var columnOptions1 = {
                           'min-row': 4,
                           'max-row': maxRows,
                           'min-col': col,
                           'max-col': col,
                           'return-empty': true
                       };
                       var columnOptions2 = {
                           'min-row': 4,
                           'max-row': maxRows,
                           'min-col': col + 1,
                           'max-col': col + 1,
                           'return-empty': true
                       };
                       sheet.getCells(columnOptions1, function(err, cells) {
                           for (var i = 0; i < cells.length; i++) {
                               var name = rowers[i];
                               if (cells[i].value === "1") {
                                   finalComingWomen[name] = "YES";
                               } else if (cells[i].value === "0") {
                                   finalComingWomen[name] = "NO";
                               } else {
                                   finalComingWomen[name] = "N/A";
                               }
                               if (i == cells.length - 1) {
                                   ticker++;
                               }
                           }
                       });

                       sheet.getCells(columnOptions2, function(err, cells) {
                           for (var i = 0; i < cells.length; i++) {
                               var name = rowers[i];
                               if (cells[i].value === "1") {
                                   finalComingOpen[name] = "YES";
                               } else if (cells[i].value === "0") {
                                   finalComingOpen[name] = "NO";
                               } else {
                                   finalComingOpen[name] = "N/A";
                               }
                               if (i == cells.length - 1) {
                                   ticker++;
                               }
                           }
                       });

                       if (ticker == 2) {
                           resolve();
                       }

                   });
               }

               var date = "6/18" //NEED TO CHANGE DATE TO REAL DATE


               //Finding Column Applicable for each data set, and then grabbing data from Google Sheets API with the column number
               var dateOptions = {
                   'min-row': 2,
                   'max-row': 2,
                   'return-empty': true
               };
               sheet.getCells(dateOptions, function(err, cells) {
                   for (var i = 0; i < cells.length; i++) {
                       if (date == cells[i].value) {
                           getMyAsync(Number(cells[i].col)).then(function() {
                               res.json({ finalComingOpen, finalComingWomen });
                           });
                           break;
                       }
                   }
               });
           }
       }

   });

@bunnyvishal6 @marzelin

I was able to work it out on my own, and it seems like everything is in order. The functions work in order, and I’m satisfied with that portion. My only problem now is that I have four variables,
womenComing womenNotComing openComing openNotComing
that all end up as blank arrays after the function runs, even though I explicitly state, if the function finds a match of a 1, array.push the corresponding rower’s name into the array womenComing. This is a small example because it goes beyond that, but see down below, it’s such a weird bug.

Note that all of the variables that you do not see declared were declared earlier in the program. The arrays that are not currently working were all declared in the same manner: var arrayX = [];

    var rowersOptions = {
      'min-row': 4,
      'max-row': maxRows, //Defined earlier
      'min-col': 2,
      'max-col': 2,
      'return-empty': true
    };
    var rowers = [];
      //Get Rowers
    sheet.getCells(rowersOptions, function(err, cells) {
      var CellData = {};
      for (var i = 0; i<cells.length; i++) {
        rowers.push(cells[i].value.toString());
      }
    });


      var date = "6/18" //NEED TO CHANGE DATE TO REAL DATE

      function getWomen (col) {
        var columnOptions1 = {
          'min-row': 4,
          'max-row': maxRows,
          'min-col': col,
          'max-col': col,
          'return-empty': true
        };
        return new Promise(function(resolve, reject) {
          sheet.getCells(columnOptions1, function(err, cells) {
            for (var i = 0; i<cells.length; i++) {
              if (cells[i].value === "1") {
                womenComing.push(rowers[i]);
                finalComingWomen[rowers[i]] = "YES"; //JSON Object defined earlier
              } else if (cells[i].value === "0") {
                womenNotComing.push(rowers[i]);
                finalComingWomen[rowers[i]] = "NO";
              } else {
                finalComingWomen[rowers[i]] = "N/A";
              }
              if (i == cells.length - 1 ){
                console.log("This should come first");
                resolve();
              }
            }
          });
        });
      }


      function getOpen (col) {
        var columnOptions2 = {
          'min-row': 4,
          'max-row': maxRows,
          'min-col': col+1,
          'max-col': col+1,
          'return-empty': true
        };
        return new Promise(function(resolve, reject) {
          sheet.getCells(columnOptions2, function(err, cells) {
            for (var i = 0; i<cells.length; i++) {
              if (cells[i].value === "1") {
                openComing.push(rowers[i]);
                finalComingOpen[rowers[i]] = "YES"; //JSON Object defined earlier
              } else if (cells[i].value === "0") {
                openNotComing.push(rowers[i]);
                finalComingOpen[rowers[i]] = "NO";
              } else {
                finalComingOpen[rowers[i]] = "N/A";
              }
              if (i == cells.length - 1 ){
                console.log("This should come second");
                resolve();
              }
            }
          });
        });
      }


      //Finding Column Applicable for each data set, and then grabbing data from Google Sheets API with the column number
      var dateOptions = {
        'min-row': 2,
        'max-row': 2,
        'return-empty': true
      };
      sheet.getCells(dateOptions, function(err, cells) {
        for (var i = 0; i<cells.length; i++) {
          if (date == cells[i].value){
          Promise.all([getWomen(Number(cells[i].col)), getOpen(Number(cells[i].col))]).then(() => {
            console.log("this should come third");
            res.json({finalComingOpen, finalComingWomen});
          }).then(emailSends());
          break;
          }
        }
      });