【问题标题】:How to call a function after an asynchronous for loop of Object values finished executing对象值的异步for循环执行完毕后如何调用函数
【发布时间】:2014-03-22 05:13:15
【问题描述】:

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

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

【问题讨论】:

    标签: javascript ajax node.js asynchronous


    【解决方案1】:

    这可以在 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 类似,但将结果数组传递给回调的第二个参数。

    如果您更喜欢 vanilla 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();
    }
    

    (取自gist我不久前写的)

    我强烈建议您使用异步库。 Async 写起来很繁琐,async.auto 这样的函数很棒。

    【讨论】:

    • 写在哪里?这似乎是个奇怪的建议。在任何情况下,异步库(npm 中第二依赖的库)是如此普遍,以至于它似乎是一个合理的例外。
    • 现在答案中有一个香草替代each 函数。
    • 使用 node.js 并拒绝使用/学习 ansyc 库似乎非常愚蠢。
    • 是的。即使您不使用 jQuery,前端工程师也有可能必须阅读和理解使用 jQuery 的外部代码库。这个类比对 Node 来说是正确的。您可以使用普通(不聪明,NIH 综合症),但在某些时候您必须阅读和理解涉及异步的代码。
    • @Mark S. Everitt 纯 JS 值得赞赏。谢谢。
    【解决方案2】:

    一个可能的简单 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);

    【讨论】:

    • 这不是问题所要求的。问题是循环之后的回调。即在所有迭代尚未完成之后,而不是在每次迭代之后。
    • 这个问题的措辞让我很困惑,即使在多次阅读之后,我也不能 100% 确定要问的是什么。
    • @Mark S. Everitt 您认为现在这是对问题的正确理解吗?
    • 它有一个在异步循环调用全部回调后运行的函数,所以如果我对问题的解释是正确的,它看起来就可以完成工作。不过,没有必要制作等待对象。您可以只测量键数组的长度并在 callOnComplete 中将其递减,当它为零时触发 func。
    • 是的,这是一个明智的建议。感谢您的反馈。
    猜你喜欢
    • 2012-07-01
    • 2013-04-23
    • 2015-07-19
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 1970-01-01
    相关资源
    最近更新 更多