AshBringer
AshBringer

Reputation: 2673

Return list from asynchronous function with nodejs

I have the following code :

var fs = require("fs");

function getMediaList(){
    var media_copy_list, line_list;
    media_copy_list = [];

    fs.readFile("input.csv", function(err, data) {

      line_list = data.toString('utf-8').trim().split('\n');
      return line_list.forEach(function(file_line) {
        var output_path, source_path, split_list;
        if (file_line.length) {
          split_list = file_line.split(';');
          console.log(split_list[0]);
          if (split_list.length >= 2) {
            source_path = split_list[0].toString('utf-8').trim();
            output_path = split_list[1].toString('utf-8').trim();
            media_copy_list.push({
              source: source_path,
              destination: output_path
            });
          }
        }
      });
    });
}

You can see that that I'm filling a list with :

media_copy_list.push({
  source: source_path,
  destination: output_path
});

What I'd like to do is to return this list once I have finished reading the input.csv file.

I don't have any issues if I read the file synchrnously( just have to call return media_copy_list). But in this case , I don't know.

I heard about async.parallel but really don't know how to apply.

Example of input.csv :

FirstPart;SecondPart

Test/test2;Whatever/example

Upvotes: 1

Views: 1225

Answers (3)

Mike Atkins
Mike Atkins

Reputation: 1591

As noted in the comments, you don't want to return the list from the function.. what you should do is include a callback as a parameter to getMediaList and call that callback with your results. I would use async.each for looping through the lines in the file. You can read more about async.each here: https://github.com/caolan/async#each. Here is an example:

var fs = require("fs");

function getMediaList(callback){
    var media_copy_list, line_list;
    media_copy_list = [];

    fs.readFile("input.csv", function(err, data) {
      if(err) {
        return callback(err);
      }
      line_list = data.toString('utf-8').trim().split('\n');
      async.each(line_list, function(file_line, next) {
        var output_path, source_path, split_list;
        if (file_line.length) {
          split_list = file_line.split(';');
          console.log(split_list[0]);
          if (split_list.length >= 2) {
            source_path = split_list[0].toString('utf-8').trim();
            output_path = split_list[1].toString('utf-8').trim();
            media_copy_list.push({
              source: source_path,
              destination: output_path
            });
          }
        }
        next(err); 
      }, function (err) {
        callback(err, media_copy_list);
      }
    });
}

Upvotes: 1

Yanick Rochon
Yanick Rochon

Reputation: 53576

Just wrap your code inside a promise and resolve it only once you're done. Some suggest callbacks, which does pretty much the same thing, but this pattern is discouraged, now. You should really use a promise.

var fs = require("fs");

function getMediaList(file){
  return new Promise(function (resolve, reject) {
    fs.readFile(file, 'utf-8', function(err, data) {
      if (err) {
        return reject(err);
      }

      resolve(data.split('\n').reduce(function(media_copy_list, file_line) {
        var output_path;
        var source_path;
        var split_list;

        file_line = file_line.trim();

        if (file_line.length) {
          split_list = file_line.split(';');
          console.log(split_list[0]);

          if (split_list.length >= 2) {
            source_path = split_list[0].toString('utf-8').trim();
            output_path = split_list[1].toString('utf-8').trim();
            media_copy_list.push({
              source: source_path,
              destination: output_path
            });
          }
        }

        return media_copy_list;
      }, []));
    });
  });
}

Then, invoke with

getMediaList('input.csv').then(function (mediaList) {
  // ...
}).catch(function (err) {
  console.error(err.stack);
});

Note: bluebird, Q, etc. are quite unnecessary since Node 4.2+. Unless you are using an earlier version of Node, try to avoid them. IMO.

The reason why Promises are encouraged is because Node will implement async/await, which will allow you to call this exact same function like :

var mediaList = await getMediaList('input.csv');

Upvotes: 1

Tudor Crisan
Tudor Crisan

Reputation: 87

Or you can use promises(bluebird in the case below).

var Promise  = require('bluebird'),
fs = require("fs"),
media_copy_list, line_list,
media_copy_list = [];

fs.readFile("input.csv", function(err, data) {
 line_list = data.toString('utf-8').trim().split('\n');
  Promise.map(line_list, function(file_line) {
    var output_path, source_path, split_list;
    if (file_line.length) {
      split_list = file_line.split(';');
      if (split_list.length >= 2) {
        source_path = split_list[0].toString('utf-8').trim();
        output_path = split_list[1].toString('utf-8').trim();
        media_copy_list = {
          source: source_path,
          destination: output_path
        };
      }
    }
    return media_copy_list 
 }).then(function(values){
    console.log(values);
 })

});

Upvotes: 0

Related Questions