pacific01
pacific01

Reputation: 47

Callback problems

I am new into javascript, and currently I'm trying to learning callback to my script. This script should return reduced words in array of objects

var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];

mapping(dict, function(error,data){
	if(error) throw error
	console.log(data)
})

function mapping(list, callback){
	var txtObj = []
	list.forEach(function (val) {
		readFile(val, function(error,data){
			txtObj.push(data)
		})
	})
	function readFile(src, cb){
		fs.readFile(src,'utf8', function (error,data) {
			if (error) return callback(error,null)
			return mapred(data)
		})
	}
	return callback(null,txtObj)
}

But it returns empty array. Any help would be appreciated. Thanks!

Upvotes: 2

Views: 90

Answers (2)

Jaffer
Jaffer

Reputation: 2968

`fs.readFile` 

is an asynchronous function, before it's done and result callback is invoked, you are returning the empty txtObj array.

how to fix it ?

call return callback(null,txtObj) after fs.readFile is finished running.

and also, as you are running asynchronous function on an array of items one-by-one, it might not still work the way you want. might want to use modudles like async in nodejs

Here comes an asynchronous version using module async. synchronous file operation is strongly objected :)

var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];

mapping(dict, function(error,data){
    if(error) throw error
    console.log(data)
})

function mapping(list, callback){
    var txtObj = [],
        async = require('async');

    async.each(list, readFile, function(err) {
        callback(err,txtObj)
    });

    function readFile(src, cb) {
        fs.readFile(src,'utf8', function (error,data) {
            if (error) {
                cb(error);
            }
            else {
                txtObj.push(mapred(data));
                cb(null);
            }
        })
    }
}

EDIT : You can do this without async, but it is little bit dirty isn't it ? also its OK if you remove the self invoking function inside the forEach, i included so that you can access the val, even after the callback is done

var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];

mapping(dict, function(error,data){
    if(error) throw error
    console.log(data)
})

function mapping(list, callback){
    var txtObj = [],
        counter = list.length,
        start = 0;

    list.forEach(function (val) {
        (function(val)
            readFile(val, function(error,data) {
                txtObj.push(data);
                start++;
                if(error || (start === counter)) {
                    callback(error,txtObj);
                }

        }))(val);
    })

    function readFile(src, cb) {
        fs.readFile(src,'utf8', function (error,data) {
            if (error) {
                cb(error);
            }
            else {
                txtObj.push(mapred(data));
                cb(null);
            }
        })
    }
}

Upvotes: 3

Matt
Matt

Reputation: 1407

The reason you are getting an empty array result is that you are performing the callback before the readFile function has a chance to populate the array. You are performing multiple asynchronous actions but not letting them to complete before continuing.

If there was only one async action, you would call callback() in the callback function of readFile, but as you need to perform multiple async actions before calling callback(), you should consider using fs.readFileSync().

Sometimes sync cannot be avoided.

function mapping(list, callback)
{
    var txtObj = []
    list.forEach(function(val)
    {
        try { txtObj.push(mapred(fs.readFileSync(val, 'utf8'))) }
        catch(err) { callback(err) }
    })
    callback(null, txtObj)
}

Upvotes: 1

Related Questions