代码之家  ›  专栏  ›  技术社区  ›  allyourcode

有没有办法在一组事件发生时设置处理函数?

  •  2
  • allyourcode  · 技术社区  · 16 年前

    例如,我有两个并发的AJAX请求,我需要这两个请求的结果来计算第三个结果。我使用的是Prototype库,所以它可能看起来像这样:

    var r1 = new Ajax.Request(url1, ...);
    var r2 = new Ajax.Request(url2, ...);
    
    function on_both_requests_complete(resp1, resp2) {
       ...
    }
    

    一种方法是使用投票,但我认为一定有更好的方法。

    更新:可接受的解决方案必须没有竞争条件。

    5 回复  |  直到 14 年前
        1
  •  1
  •   Stefan Kendall    16 年前

    在每个请求的回调函数上,设置一个布尔值,例如

    request1Complete request2Complete

    打电话 on_both_requests_complete(resp1,resp2) .

    在handler函数中,检查两个布尔值是否都已设置。如果没有,只需返回并退出函数即可。回调函数应该序列化,因为它们不能同时发生,所以这应该是可行的。如果它们可以同时发生,你会在比赛条件下破发。

        2
  •  1
  •   Justin Johnson    16 年前

    我会这样做的。该方法是一种通用的方法,它为您提供了更大的灵活性和重用性,并避免了耦合和全局变量的使用。

    var makeEventHandler = function(eventMinimum, callback) {
        var data = [];
        var eventCount = 0;
        var eventIndex = -1;
    
        return function() {
            // Create a local copy to avoid issues with closure in the inner-most function
            var ei = ++eventIndex;
            return function() {
                // Convert arguments into an array
                data[ei] = Array.prototype.slice.call(arguments);
    
                // If the minimum event count has not be reached, return
                if ( ++eventCount < eventMinimum  ) {
                    return;
                }
    
                // The minimum event count has been reached, execute the original callback
                callback(data);
            };
        };
    };
    

    一般用法:

    // Make a multiple event handler that will wait for 3 events
    var multipleEventHandler = makeMultipleEventHandler(3, function(data) {
        // This is the callback that gets called after the third event
        console.log(data);
    });
    
    multipleEventHandler()(1,2,3);
    var t = multipleEventHandler();
    setTimeout(function() {t("some string");}, 1000);
    multipleEventHandler()({a: 4, b: 5, c: 6});
    

    回调的输出(由Firebug压缩):

     [[1, 2, 3], ["some string"], [Object { a=4,  more...}]]
    

    请注意 data

    要在Ajax请求的上下文中使用它,请执行以下操作:

    var onBothComplete = makeMultipleEventHandler(2, function(data) {
        // Do something
        ...
    });
    new Ajax.Request(url1, {onComplete: onBothComplete()});
    new Ajax.Request(url2, {onComplete: onBothComplete()});
    

    编辑:我已将函数更新为强制 数据 始终以同步执行的顺序维护异步接收的事件数据(前面的警告不再存在)。

        3
  •  0
  •   Justin Johnson    16 年前

    好吧,你必须记住浏览器中的JS实现并不是真正的并发的,并利用它来发挥你的优势。所以你要做的是在每个处理程序中检查另一个是否完成了。jQuery中的示例:

    var other_done = false;
    $.get('/one', function() {
      if (other_done) both_completed();
      other_done = true;
      alert('One!');
    });
    $.get('/two', function() {
      if (other_done) both_completed();
      other_done = true;
      alert('Two!');
    });
    function both_completed() {
      alert('Both!');
    }
    
        4
  •  0
  •   Community Mohan Dere    9 年前

    基于 Justin Johnson's response to this question :

    function sync(delays /* Array of Functions */, on_complete /* Function */) {
        var complete_count = 0;
        var results = new Array(delays.length);
    
        delays.length.times(function (i) {
            function on_progress(result) {
                results[i] = result;
                if (++complete_count == delays.length) {
                    on_complete(results);
                }
            }
            delays[i](on_progress);
        });
    }
    

    var delays = [];
    delays[0] = function (on_progress) {
        new Ajax.Request(url1, {onSuccess: on_progress});
    };
    delays[1] = function (on_progress) {
        new Ajax.Request(url2, {onSuccess: on_progress});
    };
    function on_complete(results) { alert(results.inspect()); }
    sync(delays, on_complete);
    

    我不确定的一点是,这是否避免了比赛条件。如果表达式++complete_count==延迟。长度总是以原子的方式发生,那么这应该是可行的。

        5
  •  0
  •   Seaux    16 年前

    您可以使用设置临时变量并等待“最后一个”请求发出的概念。为此,您可以让这两个句柄函数将tmp变量设置为return val,然后调用“on_both_requests_complete”函数。

    var processed = false;
    var r1 = new Ajax.Request(...);
    var r2 = new Ajax.Request(...);
    
    (function() {
     var data1;
     var data2;
    
     function handle_r1(data) {
       data1 = data;
       on_both_requests_complete(); 
     };
    
     function handle_r2(data) {
       data2 = data;
       on_both_requests_complete();
     };
    
     function on_both_requests_complete() {
      if ( (!data1 || !data2) || processed) {
        return;
      }
      processed = true;
    
      /* do something */
     };
    }();