0

I'm using for loop to iterate over an array of elements and to call the same function with different parameters inside the for loop. Here is my code:

exports.listTopSongs = function(query) {
    return new Promise(function(resolve, reject) {
        var str = query.split(","), category,
        for(var i=0; i<str.length; i++) {
           sampleFn(str[i], 'sample', resolve, reject);
        }
    });
};

function sampleFn(lang, cat, resolve, reject) {
        client.on("error", function (err) {
            console.log(err);
            var err = new Error('Exception in redis client connection')
            reject(err);                                
        });
        client.keys(lang, function (err, keys){
            if (err) return console.log(err);
            if(keys.length != 0) {
                client.hgetall(keys, function (error, value) {
                    var objects = Object.keys(value);
                    result['title'] = lang;
                    result[cat] = [];
                    var x =0;
                    for(x; x<objects.length; x++) {
                        var val = objects[x];
                            User.findAll({attributes: ['X', 'Y', 'Z'],
                                where: {
                                    A: val
                                }
                            }).then(data => {
                                if(data != null) {
                                    //some actions with data and stored it seperately in a Json array
                                    if(result[cat].length == objects.length) {
                                        resolve(result);
                                    }
                                } else {
                                    console.log(""+cat+" is not avilable for this value "+data.dataValues['X']);
                                }
                            });
                    }
               });
         });
   }

Here it won't wait for completion of first iteration. It just run asyncronously before completing first iteration function. I need to return the result as result:[{ 1, 2}, {3,4}]. but it runs seamlessly and returns empty or only one object before completing all. How to resolve it.

I used node-async-loop. But it uses next and i can't able to send my parameteres while using that package. Please help me

Serge K.
  • 5,303
  • 1
  • 20
  • 27
Saravanan CM
  • 39
  • 1
  • 5
  • 17

3 Answers3

1

Async provides control flow methods allowing to do so.

Using async.each:

async.each(openFiles, function(file, callback) {

    // Perform operation on file here.
    console.log('Processing file ' + file);

    if( file.length > 32 ) {
      console.log('This file name is too long');
      callback('File name too long');
    } else {
      // Do work to process file here
      console.log('File processed');
      callback();
    }
}, function(err) {
    // if any of the file processing produced an error, err would equal that error
    if( err ) {
      // One of the iterations produced an error.
      // All processing will now stop.
      console.log('A file failed to process');
    } else {
      console.log('All files have been processed successfully');
    }
});
Léo Martin
  • 736
  • 7
  • 13
0

If you don't want to use a library, you can code it yourself. It would also be very instructive. I took your issue and coded a dummy async loop :

function listTopSongs(query) {
    return new Promise(async(resolve, reject) => { //add async here in order to do asynchronous calls
        const str = query.split(",") //str is const, and the other variable was not used anyway
        
        for( let i = 0;i < str.length; i++) {
            const planet = await sampleFn(str[i], 'sample', resolve, reject)
            console.log(planet)
        }
    });
};

function sampleFn(a, b, c, d) {
    return fetch(`https://swapi.co/api/planets/${a}/`)
        .then(r => r.json())
        .then(rjson => (a + " : " + rjson.name))
}

listTopSongs("1,2,3,4,5,6,7,8,9")

I used some dummy star wars API to fake a long promise but it should work with your sampleFn. Be careful, it is very, very slow if you have network call like the one in the example.

EDIT: I ran your code and I noticed there are a few mistakes: there is no resolve in your promise so it's not a thenable (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve see thenable )

Here is a fully working code. The nice part : no library needed, no dependencies.

//for node.js, use node-fetch :
//const fetch = require("node-fetch")

function listTopSongs(query) {
    return new Promise(async(resolve, reject) => { //add async here in order to do asynchronous calls
        const str = query.split(",") //str is const, and the other variable was not used anyway
        const planets = []
        for (let i = 0; i < str.length; i++) {
            const planet = await sampleFn(i + 1, str[i], resolve, reject)
            planets[i] = planet
            console.log(planet)
        }
        resolve(planets)
    });
};

function sampleFn(a, b, c, d) {
    return fetch(`https://swapi.co/api/planets/${a}/`)
        .then(r => r.json())
        .then(rjson => (a + b + " : " + rjson.name))
}

listTopSongs("a,b,c,d").then(planets => console.log(planets))
Théophile Pace
  • 826
  • 5
  • 14
0

Since you are using promise, you can do something like this

exports.listTopSongs = function(query) {
    return Promise.resolve(true).then(function(){
        var str = query.split(",");
        var promises = str.map(function(s){
            return sampleFn(str[i], 'sample');
        });
        return Promise.all(promises);
    }).then(function(results){
       //whatever you want to do with the result
    });
};

For this to work you have to change your sampleFn to not to depend on external resolve and reject functions. I don't see a reason using external resolve and reject. why just not use Promise.Resolve, Promise.Reject;

Manish Kumar
  • 538
  • 1
  • 7
  • 13