2011-10-12 12 views
14

Tengo un objeto javascript estándar cuyo prototipo se extiende con un método .start() tomando 2 devoluciones de llamada como argumentos: success y failure respectivamente. Este método realiza un procesamiento asincrónico (es no AJAX) y, dependiendo del resultado de este procesamiento, invoca las devoluciones de llamada correctas o fallidas.Cómo usar el objeto diferido de jQuery con objetos JavaScript personalizados?

Así es como esto podría ser esquematizada:

function MyObject() { 
} 

MyObject.prototype.start = function(successCallback, errorCallback) { 
    (function(s, e) { 
     window.setTimeout(function() { 
      if (Math.random() < 0.8) { 
       s();  
      } else { 
       e();  
      } 
     }, 2000);  
    })(successCallback, errorCallback); 
} 

No es realmente importante el procesamiento exacta realizada dentro del método, sólo que es asíncrona y sin bloqueo. No tengo control sobre el momento en que el método de inicio terminará el procesamiento. Tampoco tengo control sobre el prototipo y la implementación de este método.

Lo que tengo control es el success y failure devoluciones de llamada. Depende de mí proporcionarlos.

Ahora I tienen una serie de dichos objetos:

var arr = [ new MyObject(), new MyObject(), new MyObject() ]; 

El orden de los elementos de esta matriz es importante. Necesito activar el método .start() en cada elemento de la matriz de forma consecutiva, pero solo una vez que se haya completado la anterior (es decir, se llamó a la devolución de llamada exitosa). Y si ocurre un error (se llama a la devolución de llamada fallida) quiero detener la ejecución y no invocar el método .start en los elementos restantes de la matriz.

pude poner en práctica esta ingenua mediante el uso de una función recursiva:

function doProcessing(array, index) { 
    array[index++].start(function() { 
     console.log('finished processing the ' + index + ' element'); 
     if (index < array.length) { 
      doProcessing(array, index); 
     } 
    }, function() { 
     console.log('some error ocurred'); 
    }); 
} 

doProcessing(arr, 0); 

Esto funciona bien, pero mirando el jQuery's deferred Object que se introdujo en jQuery 1.5 Creo que hay un margen de mejora de este código. Lamentablemente, aún no me siento muy cómodo con eso y estoy tratando de aprenderlo.

Así que mi pregunta es si es posible adaptar mi código ingenuo y aprovechar esta nueva API y, en caso afirmativo, ¿podría darme algunos consejos?

Aquí hay un jsfiddle con mi implementación.

+0

1 siempre es bueno ver un contestador arriba haciendo una buena pregunta. ¿Respondiendo uno a este? –

Respuesta

4

se podría hacer algo como esto: (jsFiddle)

function MyObject() { 
} 

MyObject.prototype.start = function(queue) { 
    var deferred = $.Deferred(); 
    //only execute this when everything else in the queue has finished and succeeded 
    $.when.apply(jQuery,queue).done(function() { 
     window.setTimeout(function() { 
      if (Math.random() < 0.8) { 
       deferred.resolve();  
      } else { 
       deferred.reject();  
      } 
     }, 2000); 
    }); 
    return deferred; 
} 

var arr = [ new MyObject(), new MyObject(), new MyObject() ]; 

var queue = new Array(); 
$.each(arr, function(index, value) { 
    queue.push(value.start(queue) 
     .done(function() { 
      console.log('succeeded ' + index); 
     }) 
     .fail(function() { 
      console.log('failed ' + index); 
     })); 
}); 

No del todo seguro ya sea que se consideraría esto una mejora, sin embargo.

+0

Parece interesante, el único problema es que no puedo modificar 'MyObject'. –

+0

¿Cuál es tu objetivo final por cierto? El verdadero poder de los diferidos radica en el hecho de que puede registrar múltiples devoluciones de llamadas en colas de devolución de llamada. Por lo tanto, independientemente de que la (a) función síncrona ya haya finalizado, aún puede registrar devoluciones de llamada adicionales. Además, es muy fácil registrar múltiples devoluciones de llamada. ¿Esto suena como algo que necesitarías? –

+0

mi verdadero objetivo es aprender cómo funciona Deffered tratando de ver si se puede aplicar a algún escenario. Tu respuesta es muy útil en esta dirección. Gracias. –

2

No hay nada de malo en su implementación. Y como todos sabemos, usar jQuery no siempre es el mejor método.

lo haría así: (sin la necesidad de modificar la clase MiObjeto ..)

function doProcessing(array, index) { 
    var defer = new $.Deferred(); 

    $.when(defer).then(doProcessing); 

    array[index++].start(function() { 
     log('finished processing the ' + index + ' element'); 
     if (index < array.length) { 
      defer.resolve(array, index); 
     } 
    }, function() { 
     log('some error ocurred => interrupting the process'); 
    }); 
}; 

Como se puede ver, no hay ninguna ventaja real sobre la llanura JavaScript método. :)

Aquí es mi violín: http://jsfiddle.net/jwa91/EbWDQ/

3

Cuando programamos, para recordar los principios o directrices GRASP es muy importante.

http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)

para obtener alta cohesión y bajo acoplamiento significa que nuestro código será mejor, más reutilizables y fáciles de mantener.

Por lo tanto, la clase MyObject no debe conocer la existencia de la cola. MyObject conocerá sus propias características y métodos, y mucho más.

// Class MyObject 

function MyObject(name) { 
    this.name = name; 
} 

MyObject.prototype.start = function() { 

    var deferred = $.Deferred(); 
    var self = this; 
    setTimeout(function() { 
     if (Math.random() <= 0.8) { 
      console.log(self.name + "... ok"); 
      deferred.resolve(); 
     } else { 
      console.log(self.name + "... fail"); 
      deferred.reject(); 
     } 
    }, 1000); 

    return deferred.promise(); 
} 

La función principal/persona que llama sabrá MiObjeto existencia y creará tres casos que van a ser ejecutados secuencialmente.

// Create array of instances 
var objectArray = [ new MyObject("A"), new MyObject("B"), new MyObject("C") ]; 

// Create array of functions to call start function 
var functionArray = []; 
$.each(objectArray, function(i, obj) { 
    functionArray.push(
     function() { 
      return obj.start(); 
     } 
    ); 
}); 

// Chain three start calls 
$.iterativeWhen(functionArray[0], functionArray[1], functionArray[2]) 
.done(function() { 

    console.log("First: Global success"); 

    // Chain three start calls using array 
    $.iterativeWhen.apply($, functionArray) 
    .done(function() { 
     console.log("Second: Global success"); 
    }) 
    .fail(function() { 
     console.log("Second: Global fail"); 
    }); 

}) 
.fail(function() { 
    console.log("First: Global fail"); 
}); 

He creado un complemento para jQuery: iterativeWhen. Funciona con jQuery 1.8 y versiones posteriores.

$.iterativeWhen = function() { 

    var deferred = $.Deferred(); 
    var promise = deferred.promise(); 

    $.each(arguments, function(i, obj) { 

     promise = promise.then(function() { 
      return obj(); 
     }); 
    }); 

    deferred.resolve(); 

    return promise; 
}; 

jsFiddle aquí: http://jsfiddle.net/WMBfv/

Cuestiones relacionadas