javascriptnode.jsco

Console.log shows array but can't be returned


I'm new to working with promises (I'm using 'co' in node) so I'm not entirely sure what's failing with this code:

function* excelToJSON(excelFileNames) {
  var jsonData = [];

  for (let index = 0; index < excelFileNames.length; index++) {
    parseXlsx(excelFilesNames[index], function (err, data) {

      jsonData.push(data);
      console.log(jsonData); //***Shows data correctly
    });
  }

  console.log(jsonData); //***Empty array
  return yield jsonData;
}

It reads the file, converts it and, at least within the loop, it shows everything correctly, but once we get out of the loop the data seems to disappear. I've also tried to return one of the values from within the loop but that doesn't work either.

EDIT: parseXlsx is from the 'excel' module here: https://github.com/trevordixon/excel.js I'm not entirely sure if it's async or sync, to be honest. This seems to be its code, and I know 'extractFiles' returns a promise but since it then goes through 'parseXlsx' I'm not sure what happens afterwards:

function parseXlsx(path, sheet, cb) {
  if (typeof cb === 'undefined') {
    cb = sheet;
    sheet = '1';
  }
  extractFiles(path, sheet).then(function(files) {
    cb(null, extractData(files));
  },
  function(err) {
    cb(err);
  });
};

EDIT2: What I used to solve it is a combination of several answers, thanks to all of you.

function* excelToJSON(excelFileNames) {
  return new Promise(function(resolve, reject) {
    var jsonData = [];

    if (excelFilesNames === null || excelFilesNames.length === 0) {
      reject();
    }

    for (let index = 0; index < excelFilesNames.length; index++) {
      parseXlsx(excelFilesNames[index], function(err, data) {
        if (err) {
          throw err;
        }

        jsonData.push(data);

        if (jsonData.length === excelFilesNames.length) {
          resolve(jsonData);
        }
      });
    }
  });
}


Solution

  • So what's happening here is that your code just runs through that for block, invoking parseXlsx a few times, but never actually waits for it to finish.

    So this is why your empty array log comes first, and then there come the logs with the 'correct data'. Look up javascript event loop to get a better understanding of how asynchronous functions work.

    What you essentially need is either a promise that you resolve when you're done, or get a callback function that you'll call when you're done.

    And you'll know when you're done when your jsonData.push(data); has been called as many times as long as your excelFileNames array is.

    For example:

    function excelToJSON(excelFileNames) {
      var deferred = Promise.defer();
      var jsonData = [];
    
      for (let index = 0; index < excelFileNames.length; index++) {
        parseXlsx(excelFilesNames[index], function (err, data) {
    
          jsonData.push(data);
          console.log(jsonData); //***Shows data correctly
          if (jsonData.length === excelFileNames.length) {
            deferred.resolve(jsonData);
          }
        });
      }
    
      return deferred.promise;
    }
    
    // And use it as a promise:
    
    var exelToJsonPromise = excelToJSON(["apples.xlsx", "pears.xlsx]);
    
    exelToJsonPromise.then(function(jsonData){ 
      console.log(jsonData); // Now this will have everything in it.
    });