简体   繁体   中英

Node for-loop callback required

I want to change the Tags format which I am fetching form one of the collections. Tags data contains some KC ids in an array which I am using to get KC data and insert in TagUnit to get final response format.

var newTags = Tags.map(function(TagUnit) {
    for (var i = 0; i < TagUnit.kcs.length; i++) {
        KCArray = [];
        KC.findById(TagUnit.kcs[i], function(error, data) {
            KCMap = {};
            KCMap['kc_id'] = data._id;
            KCMap['kc_title'] = data.title;
            KCArray.push(KCMap);
            if (KCArray.length == TagUnit.kcs.length) {
                TagUnit.kcs = KCArray;
            }
        });
     }
     return TagUnit;
});

response.send(JSON.stringify(newTags));

But I am not getting desired result. Response is giving out Tag data in initial for instead of formatted form. I guess it is due to event looping. I will be grateful if someone can help me with this.

**Edit: ** I am using MongoDB as database and mongoose as ORM.

You can use promises or Async module

var async = require('async');
...
function getKC (kc, callback) {
   KC.findById(kc, function(err, data) {
       if (err)
          return callback(err);

       callback(null, {kc_id: data._id, kc_title: data.title})
   });
}

function getKCs (tag, callback) {
   async.map(tag.kcs, getKC, callback);
}

async.map(Tags, getKCs, function(err, results){
    if (err)
       return console.log(err.message);

    res.json(results); // or modify and send
});

PS Perhaps, code contains errors. I cann't test it.

I'd suggest using promises to manage your async operations which is now the standard in ES6. You don't say what database you're using (it may already have a promise-based interface). If it doesn't, then we manually promisify KC.findById() :

function findById(key) {
    return new Promise(function(resolve, reject) {
        KC.findById(key, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

Then, assuming you can do all these find operations in parallel, you can use Promise.all() to both keep track of when they are all done and to order them for you.

var allPromises = Tags.map(function(TagUnit) {
    var promises = TagUnit.kcs.map(function(key) {
        return findById(key).then(function(data) {
            // make resolved value be this object
            return {kc_id: data._id, kc_title: data.title};
        });
    });
    // this returns a promise that resolves with an array of the kc_id and kc_title objects
    return Promise.all(promises).then(function(results) {
        return {
             _id: TagUnit._id,
             kcs: results
        };
    });
});

// now see when they are all done
Promise.all(allPromises).then(function(results) {
    response.send(JSON.stringify(results));
}).catch(function(err) {
    // send error response here
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM