2011-10-12 11 views
14

Ho un oggetto javascript standard il cui prototipo è esteso con un metodo .start() che accetta 2 callback come argomenti: success e failure rispettivamente. Questo metodo esegue un'elaborazione asincrona (è non AJAX) e in base al risultato di questa elaborazione richiama il callback di esito positivo o di errore.Come utilizzare l'oggetto posticipato di jQuery con oggetti javascript personalizzati?

Ecco come questo potrebbe essere schematizzato:

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

Non è veramente importante trattamento esatto eseguito all'interno del metodo, solo che è asincrono e non bloccante. Non ho il controllo sul momento in cui il metodo di avvio terminerà l'elaborazione. Inoltre, non ho alcun controllo sul prototipo e sull'implementazione di questo metodo.

Quello di cui ho il controllo sono i callback success e failure. Spetta a me fornirli.

ora ho un array di tali oggetti:

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

L'ordine degli elementi in questo array è importante. Devo attivare il metodo .start() su ogni elemento dell'array in modo consecutivo, ma solo una volta che il precedente è stato completato (cioè è stato chiamato il callback di successo). E se si verifica un errore (viene chiamato il callback di errore), voglio interrompere l'esecuzione e non richiamare più il metodo .start sugli altri elementi dell'array.

ho potuto implementare questa ingenuamente utilizzando una funzione ricorsiva:

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

Questo funziona bene, ma guardando il jQuery's deferred Object che è stato introdotto in jQuery 1.5 Penso che ci sia un margine di miglioramento di questo codice. Purtroppo non mi sento molto a mio agio con esso e sto cercando di impararlo.

Quindi la mia domanda è: è possibile adattare il mio codice naive e sfruttare questa nuova API e se sì, potresti fornirmi alcuni suggerimenti?

Ecco uno jsfiddle con la mia implementazione.

+0

+1 sempre bello vedere un top risposto chiedendo una buona domanda. Rispondendo da solo a questo? –

risposta

4

Si potrebbe fare qualcosa di simile: (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); 
     })); 
}); 

Non certo tempo che si dovrebbe considerare questo un miglioramento, però.

+0

Sembra interessante, l'unico problema è che non posso modificare 'MyObject'. –

+0

Qual è il tuo endgoal a proposito? Il vero potere dei differiti risiede nel fatto che è possibile registrare più callback in code di callback. Quindi, indipendentemente dalla (a) funzione sincrona già terminata, è ancora possibile registrare ulteriori callback. Inoltre è molto facile registrare più callback. Suona come qualcosa di cui avresti bisogno? –

+0

il mio vero obiettivo è imparare come funziona Deffered cercando di vedere se potrebbe essere applicato ad alcuni scenari. La tua risposta è molto utile in questa direzione. Grazie. –

2

Non c'è niente di sbagliato nella tua implementazione. E come tutti sappiamo, usare jQuery non è sempre il metodo migliore.

lo farei in questo modo: (senza la necessità di modificare la classe MyObject ..)

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

Come si può vedere, non c'è alcun vantaggio reale sulla pianura JavaScript metodo. :)

Ecco il mio violino: http://jsfiddle.net/jwa91/EbWDQ/

3

Quando si programma, per ricordare i principi afferrare o linee guida è molto importante.

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

per ottenere elevati di coesione e Low Coupling significa che il nostro codice sarà migliore, più riutilizzabile e facile da mantenere.

Pertanto, la classe MyObject non deve conoscere l'esistenza della coda. MyObject conoscerà le sue caratteristiche e metodi e altro ancora.

// 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 funzione principale/chiamante saprà MyObject esistenza e creerà tre istanze che saranno eseguite in modo sequenziale.

// 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"); 
}); 

ho costruito un plugin per jQuery: iterativeWhen. Funziona con jQuery 1.8 e versioni successive.

$.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 qui: http://jsfiddle.net/WMBfv/

Problemi correlati