简体   繁体   中英

How to call a function after an asynchronous for loop of Object values finished executing

I want to call a function after an asynchronous for loop iterating through values of an Javascript object finishes executing. I have the following code

for (course in courses) {
    var url = '...' + courses[course];

    request(url, (function (course) {
        return function (err, resp, body) {
            $ = cheerio.load(body);

            //Some code for which I use object values    
        };
    })(course));
}

This can be done in vanilla JS, but I recommend the async module, which is the most popular library for handling async code in Node.js. For example, with async.each :

var async = require('async');

var courseIds = Object.keys(courses);

// Function for handling each course.
function perCourse(courseId, callback) {
    var course = courses[courseId];

    // do something with each course.
    callback();
}

async.each(courseIds, perCourse, function (err) {
    // Executed after each course has been processed.
});

If you want to use a result from each iteration, then async.map is similar, but passes an array of results to the second argument of the callback.

If you prefer vanilla JS, then this will work in place of async.each :

function each(list, func, callback) {
    // Avoid emptying the original list.
    var listCopy = list.slice(0);

    // Consumes the list an element at a time from the left.
    // If you are concerned with overhead in using the shift
    // you can accomplish the same with an iterator.
    function doOne(err) {
        if (err) {
            return callback(err);
        }

        if (listCopy.length === 0) {
            return callback();
        }

        var thisElem = listCopy.shift();

        func(thisElem, doOne);
    }

    doOne();
}

(taken from a gist I wrote a while back)

I strongly suggest that you use the async library however. Async is fiddly to write, and functions like async.auto are brilliant.

A possible simple JS solution would be to do something like this.

 var courses = { lorum: 'fee', ipsum: 'fy', selum: 'foe' }; var keys = Object.keys(courses); var waiting = keys.length; function completedAll() { console.log('completed all'); } function callOnCourseComplete(course, func) { console.log('completed', course); waiting -= 1; if (!waiting) { func(); } } var delay = 10000; keys.forEach(function(course) { var url = '...' + courses[course]; console.log('request', url); setTimeout((function(closureCourse) { return function( /* err, resp, body */ ) { // Some code for which I use object values callOnCourseComplete(closureCourse, completedAll); }; }(course)), (delay /= 2)); }); 

Update: Probably a better Javascript solution would be to use Promise s

 const courses = { lorum: 'fee', ipsum: 'fy', selum: 'foe', }; function completedAll() { console.log('completed all'); } function callOnCourseComplete(courseName) { console.log('completed', courseName); } let delay = 10000; const arrayOfPromises = Object.keys(courses).map(courseName => ( new Promise((resolve, reject) => { const url = `...${courses[courseName]}`; console.log('request', url); setTimeout((err, resp, body) => { if (err) { reject(err); } // Some code for which I use object values resolve(courseName); }, (delay /= 2)); })) .then(callOnCourseComplete)); Promise.all(arrayOfPromises) .then(completedAll) .catch(console.error); 

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