0

I'm trying to loop through objects and call a method ("search") which is async and has a callback and when it is done with all objects, write the result object which is resultSet in a json file; but now the problem is since my search function is async writefile does not wait for them to finish and then write resultset to a json file, so it is writing empty objects to a json file; also it is worth mentioning I don't want to use promises; could you please let me know apart from promises what is the best approach to cope with this problem?

router.post('/export', function (req, res) {
    Object.keys(models).forEach(function (name) {
        resultSet[name] = [];
        search(param1, name, function (err, data) {
            if (err) {
                console.error(req.host, req.path, err, err.stack);
                res.json(400, err);
            } else {
                resultSet[name] = data;
            }
        });
    });

    var file = "myFile.json";

    jf.writeFile(file,resultSet,function(err){
        console.log(err);
    });      
});
5
  • Can you change the search function to take in the whole set of data, and do the looping inside the search function? Then give it a callback, which writes the resultSet after the function is done. Commented Dec 1, 2014 at 17:24
  • No it is used in many other places! Commented Dec 1, 2014 at 17:25
  • Promises are really the best solution. Why don't you want to use that approach? Commented Dec 1, 2014 at 17:25
  • you can create a counter and fired the writeFile when counter says.. Commented Dec 1, 2014 at 17:25
  • Have you tried using the async library to manage this fan-out better? async.map does a great job as an asynchronous forEach equivalent. Commented Dec 1, 2014 at 17:29

1 Answer 1

1

change to this:

function auxFunc(resultSet){
    var file = 'myFile.json';

    jf.writeFile(file, resultSet, function (err) {
        console.log(err);
    })
}

router.post('/export', function (req, res) {
    var keys = Object.keys(models);
    var counter = 1;
    keys.forEach(function (name) {
        resultSet[name] = []; 
        search(param1, name, function (err, data) {
            if (err) {
                console.error(req.host, req.path, err, err.stack);
                res.json(400, err);
            } else {
                resultSet[name] = data;
                if(counter++ === keys.length)
                   auxFunc(resultSet);
            }
        });
    });
});

This is a control flow problem.. Callbacks in a loop create a race condition, you have a lot of 'processes' working by it self, so with a counter you would know and detect when all processes have finished, and then you fire the function you need.

Is not necessary to be a counter all the times that you are working with callbacks, depending the situation you may need to process the info and depending on the result fired an action, this could be achieve with a function returning a boolean value, so the trick with callbacks is have always a condition in somewhere to control the flow.

Sign up to request clarification or add additional context in comments.

Comments