22

我有两个抽象进程(例如,在 js 对象中使用不暴露其内部的显示模块模式进行管理),它们在完成时触发自定义事件。我想在两个自定义事件都触发时执行一个操作。

jQuery 1.5 中新的Deferred逻辑似乎是一种理想的管理方式,除了 when() 方法采用返回 promise() 的 Deferred 对象(或普通的 js 对象,但随后 when() 立即完成而不是等待,这对我没用)。

理想情况下,我想做类似的事情:

//execute when both customevent1 and customevent2 have been fired
$.when('customevent1 customevent2').done(function(){
  //do something
});

将这两种技术结合起来的最佳方式是什么?

4

2 回答 2

40

http://jsfiddle.net/ch47n/

我创建了一个小插件,它创建了一个新的 jQuery.fn.when 方法。

语法是:

jQuery( "whatever" ).when( "event1 event2..." ).done( callback );

它在内部广泛使用 jQuery.when() 并确保在解析之前已在集合中的所有元素上触发了所有事件。


实际插件代码如下:

( function( $ ) {

    $.fn.when = function( events ) {

        var deferred, $element, elemIndex, eventIndex;

        // Get the list of events
        events = events.split( /\s+/g );

        // We will store one deferred per event and per element
        var deferreds = [];

        // For each element
        for( elemIndex = 0; elemIndex < this.length; elemIndex++ ) {
            $element = $( this[ elemIndex ] );
            // For each event
            for ( eventIndex = 0; eventIndex < events.length; eventIndex++ ) {
                // Store a Deferred...
                deferreds.push(( deferred = $.Deferred() ));
                // ... that is resolved when the event is fired on this element
                $element.one( events[ eventIndex ], deferred.resolve );
            }
        }

        // Return a promise resolved once all events fired on all elements
        return $.when.apply( null, deferreds );
    };

} )( jQuery );
于 2011-02-16T03:43:41.617 回答
12

您可以让“customevent1”和“customevent2”的事件处理程序在它们触发时分别发出“Deferred”实例的信号。您可以使用 "$.when()" 然后将这两者合二为一,这就是您仅在触发两个自定义事件后才触发处理程序的地方。

var df1 = $.Deferred(), df2 = $.Deferred();
$('whatever').bind('customevent1', function() {
  // code code code
  df1.resolve();
}).bind('customevent2', function() {
  // code code code
  df2.resolve();
});

var whenBoth = $.when(df1, df2);

whenBoth.then(function() {
  // code to run after both "customevent1"
  // and "customevent2" have fired
});

旧答案,为了完整起见

您可以制作自己的 Deferred 对象来跟踪这两个条件并在两者都设置时触发“resolve”:

function watchEvents() {
  var df = $.Deferred();

  var flags = {};
  $.each(Array.prototype.slice.call(arguments, 0), function() {
    flags[this] = false;
  });

  var realResolve = df.resolve.bind(df);
  df.resolve = function(eventName) {
    flags[eventName] = true;
    for (var ev in flags) if (flags[ev] === false) return;
    realResolve();
  };

  return df;
}

现在您可以调用该函数:

var df = watchEvents("customevent1", "customevent2");

现在,这些事件的事件处理程序只需要在捕获事件时对该事物调用“resolve”即可:

    df.resolve(event.type);

每个处理程序报告自己的类型。只有当您调用“watchEvents”时请求的所有事件类型都发生时,您在“df”上注册的处理函数才会真正被调用。

我突然想到,您可以做的另一件事是编写一个 jQuery 插件,为元素初始化一个 Deferred 对象,并将其存储在“.data()”属性中。然后,您可以编写更多插件,事件处理程序可以使用这些插件来发出信号,并编写其他插件来为多事件序列注册处理程序。我想那会很酷,但我需要花一些时间思考它。

于 2011-02-15T20:56:09.603 回答