Tengo dos procesos abstractos (por ejemplo, administrados dentro de los objetos js usando el patrón de módulo revelador que no expone sus partes internas) que activan eventos personalizados cuando se completan. Quiero realizar una acción cuando se hayan disparado ambos eventos personalizados.
La nueva lógica Diferida en jQuery 1.5 parece ser una forma ideal de manejar esto, excepto que el método when() toma objetos Diferidos que devuelven una promesa() (u objetos js normales, pero luego when() se completa inmediatamente en lugar de esperando, que es inútil para mí).
Idealmente me gustaría hacer algo como:
//execute when both customevent1 and customevent2 have been fired $.when('customevent1 customevent2').done(function(){ //do something });¿Cuál sería la mejor manera de combinar estas dos técnicas?
Creé un pequeño complemento que crea un nuevo método jQuery.fn.when.
La sintaxis es:
jQuery( "whatever" ).when( "event1 event2..." ).done( callback );Utiliza jQuery.when() de forma extensiva internamente y garantiza que todos los eventos se hayan activado en todos los elementos de la colección antes de resolverse.
Código de complemento real a continuación:
( 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 );Puede hacer que los controladores de eventos para "customevent1" y "customevent2" indiquen cada uno una instancia "Diferida" cuando se activan. Puede usar "$.when()" para combinar esos dos en uno, y ahí es donde pone el controlador para que se active solo después de que ambos eventos personalizados se hayan activado.
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 });Respuesta anterior, aquí para completar
Puede crear su propio objeto diferido que realiza un seguimiento de las dos condiciones y activa la "resolución" cuando ambas están configuradas:
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; }Ahora puedes llamar a esa función:
var df = watchEvents("customevent1", "customevent2");Y ahora sus controladores de eventos para esos eventos solo necesitan llamar a "resolver" en esa cosa cuando capturan los eventos:
df.resolve(event.type);Cada manejador reporta su propio tipo. Solo cuando todos los tipos de eventos solicitados cuando llamó a "watchEvents" hayan ocurrido, se llamará a las funciones del controlador que registró en "df".
Se me ocurre que otra cosa que podrías hacer sería escribir un complemento jQuery que inicialice un objeto diferido para elementos y lo almacene en una propiedad ".data()". Luego, puede escribir algunos complementos más que los controladores de eventos pueden usar para señalarse a sí mismos y otros complementos para registrar controladores para secuencias de múltiples eventos. Eso sería muy bueno, creo, pero necesito pasar algún tiempo reflexionando sobre ello.