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

时间:2010-03-15 20:18:45

标签: javascript ajax events prototypejs

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

var r1 = new Ajax.Request(url1, ...);
var r2 = new Ajax.Request(url2, ...);

function on_both_requests_complete(resp1, resp2) {
   ...
}

一种方法是使用民意调查,但我认为必须有更好的方法。

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

5 个答案:

答案 0 :(得分:1)

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

request1Completerequest2Complete

并致电on_both_requests_complete(resp1,resp2)

在处理程序函数中,检查是否设置了两个布尔值。如果没有,只需返回并退出该功能。回调函数应该被序列化,因为它们不能同时发生,所以这应该有效。如果它们可以并行发生,那么你就会在竞争条件下中断。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

嗯,你必须记住,浏览器中的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!');
}

答案 3 :(得分:0)

您可以使用设置临时变量的概念并等待“最后”请求。为此,您可以让两个句柄函数将tmp变量设置为返回值,然后调用“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 */
 };
}();

答案 4 :(得分:0)

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

这假设每个延迟都接受一个参数:一个“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 == delays.length总是以原子方式发生,那么这应该可行。