最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to call a function after an asynchronous for loop of Object values finished executing - Stack Overflow

programmeradmin4浏览0评论

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));
}

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));
}
Share Improve this question edited Feb 21, 2014 at 6:39 Aziz Shaikh 16.5k11 gold badges64 silver badges81 bronze badges asked Feb 20, 2014 at 23:08 SetrinoSetrino 3414 silver badges9 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

This can be done in vanilla JS, but I remend 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 acplish 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 pletedAll() {
  console.log('pleted all');
}

function callOnCourseComplete(course, func) {
  console.log('pleted', 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, pletedAll);
    };
  }(course)), (delay /= 2));
});

Update: Probably a better Javascript solution would be to use Promises

const courses = {
  lorum: 'fee',
  ipsum: 'fy',
  selum: 'foe',
};

function pletedAll() {
  console.log('pleted all');
}

function callOnCourseComplete(courseName) {
  console.log('pleted', 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(pletedAll)
  .catch(console.error);

发布评论

评论列表(0)

  1. 暂无评论