繁体   English   中英

确定javascript异步操作的结束

[英]determining the end of asynchronous operations javascript

如果我有一个传递了这个函数的函数:

function(work) {
   work(10);
   work(20);
   work(30);
}

(可以有任意数量的work电话,其中包含任何数字。)

work性能一些异步活动 - 比如,对于这个例子,它只是一个timeout 我有过什么样的完全控制work对这一操作的完成做(和,事实上,它的一般定义)。

确定何时完成所有work呼叫的最佳方法是什么?


我的当前方法在调用工作时递增计数器,在完成时递减计数器,并在计数器为0时触发all work done事件(在每次递减后检查)。 但是,我担心这可能是某种竞争条件。 如果情况并非如此,请展示我的原因,这将是一个很好的答案。

没有竞争条件。 每个请求在完成时执行减量的要求都增加了( 总是!包括http失败,很容易忘记)。 但是,可以通过包装调用以更封装的方式处理。

未经测试,但这是要点(我已经实现了一个对象而不是一个计数器,所以理论上你可以扩展它以获得关于特定请求的更细粒度的查询):

var ajaxWrapper = (function() {
   var id = 0, calls = {};
   return {
      makeRequest: function() {
         $.post.apply($, arguments); // for example
         calls[id] = true;
         return id++;
      },
      finishRequest: function(id) {
         delete calls[id];
      },
      isAllDone: function(){
         var prop;
         for(prop in calls) {
            if(calls.hasOwnProperty(prop)) {return false;}
         }
         return true;
      }
   };
})();

用法:

而不是$.post("url", ... function(){ /*success*/ } ... ); 干的好

var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
   function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );

如果你想要更复杂,你可以在包装器中自己添加对finishRequest的调用,因此使用几乎完全透明:

 ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );

你有很多方法可以编写这个程序,但是你使用计数器的简单技巧就可以了。

重要的是要记住,这将起作用的原因是因为Javascript在单个线程中执行 所有浏览器和node.js AFAIK都是如此。

基于下面的深思熟虑的评论,解决方案可行,因为JS事件循环将按以下顺序执行函数:

  1. 功能(工作)
  2. 工作(10)
  3. 计数器++
  4. 启动异步功能
  5. 工作(20)
  6. 计数器++
  7. 启动异步功能
  8. 工作(30)
  9. 计数器++
  10. 启动异步功能
  11. - 退回到事件循环 -
  12. 异步功能完成
  13. 计数器 -
  14. - 退回到事件循环 -
  15. 异步功能完成
  16. 计数器 -
  17. - 退回到事件循环 -
  18. 异步功能完成
  19. 计数器 -
  20. 计数器为0,因此您可以触发完成工作的消息
  21. - 退回到事件循环 -

我有一个after效用函数。

var after = function _after(count, f) {
  var c = 0, results = [];
  return function _callback() {
    switch (arguments.length) {
      case 0: results.push(null); break;
      case 1: results.push(arguments[0]); break;
      default: results.push(Array.prototype.slice.call(arguments)); break;
    }
    if (++c === count) {
      f.apply(this, results);
    }
  };
};

以下代码将起作用。 因为javascript是单线程的。

function doWork(work) {
  work(10);
  work(20);
  work(30);
}

WorkHandler(doWork);

function WorkHandler(cb) {
  var counter = 0,
      finish;
  cb(function _work(item) {
    counter++;
    // somethingAsync calls `finish` when it's finished
    somethingAsync(item, function _cb() {
      finish()
    });
  });
  finish = after(counter, function() {
    console.log('work finished');
  });
};

我想我应该解释一下。

我们将有效的函数传递给workhandler。

工作处理程序调用它并传入工作。

执行工作的函数调用多次递增计数器

由于工作的函数不是异步的(非常重要),我们可以在完成后定义完成函数。

正在完成的异步工作无法在当前同步工作块(整个工作处理程序的执行)完成之前完成(并调用未定义的完成函数)。

这意味着在整个workhandler完成后(并且设置了变量finish),异步工作作业将开始结束并调用finish。 只有当所有人都呼叫完成after ,回调才会发送到火灾after

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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