如何在对象值的异步 for 循环完成执行后调用函数



我想在异步 for 循环遍历 Javascript 对象的值完成执行后调用一个函数。我有以下代码

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

这可以在 vanilla JS 中完成,但我推荐 async 模块,它是在 Node.js 中处理异步代码的最流行的库。例如,对于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.
});

如果要使用每次迭代的结果,则async.map类似,但将结果数组传递给回调的第二个参数。

如果您更喜欢香草 JS,那么这将代替 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();
}

(摘自我前段时间写的要点)

但是,我强烈建议您使用异步库。异步编写起来很繁琐,像async.auto这样的功能非常出色。

一个可能的简单JS解决方案是做这样的事情。

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

更新:可能更好的Javascript解决方案是使用Promises。

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

最新更新