2011-10-12 9 views
14

Mam standardowy obiekt javascript, którego prototyp jest rozszerzany o metodę .start(), przyjmującą 2 wywołania zwrotne odpowiednio jako argumenty: success i failure. Ta metoda wykonuje pewne asynchroniczne przetwarzanie (jest to A31X), a w zależności od wyniku tego przetwarzania wywołuje zarówno powodzenie, jak i niepowodzenie wywołania zwrotnego.Jak korzystać z odroczonego obiektu jQuery za pomocą niestandardowych obiektów javascript?

Oto jak można to schematycznie:

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

To naprawdę nie jest ważne dokładne przetwarzanie wykonywane wewnątrz metody, tylko że to jest asynchroniczne i bez blokowania. Nie mam kontroli nad momentem, w którym metoda początkowa zakończy przetwarzanie. Nie mam też kontroli nad prototypem i wdrożeniem tej metody.

Mam kontrolę nad połączeniami zwrotnymi success i failure. To zależy od mnie, aby je zapewnić.

Teraz mam tablicę tych obiektów:

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

Kolejność elementów w tej tablicy jest bardzo ważne. Potrzebuję wywołać metodę .start() na każdym elemencie tablicy kolejno, ale tylko po zakończeniu poprzedniego (to jest wywołanie zwrotne sukcesu). A jeśli wystąpi błąd (wywoływane jest wywołanie zwrotne niepowodzenia) Chcę zatrzymać wykonywanie i nie wywołuję metody .start na pozostałych elementach tablicy.

mogłaby wdrożyć to naiwnie za pomocą funkcji rekurencyjnej:

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

Działa to dobrze, ale patrząc na jQuery's deferred Object, który został wprowadzony w jQuery 1.5 Myślę, że istnieje możliwość poprawy tego kodu. Niestety nie czuję się jeszcze z nim dobrze i staram się go nauczyć.

Moje pytanie brzmi: czy możliwe jest dostosowanie mojego naiwnego kodu i skorzystanie z tego nowego API, a jeśli tak, czy mógłbyś podać mi jakieś wskazówki?

Oto jsfiddle z moją implementacją.

+0

+1 Zawsze dobrze jest zobaczyć górną odpowiadającego prosząc dobre pytanie. Samodzielna odpowiedź na to pytanie? –

Odpowiedz

4

Można zrobić coś takiego: (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); 
     })); 
}); 

niekoniecznie pewny pogoda byłoby rozważyć ten lepszy, choć.

+0

To wygląda interesująco, jedynym problemem jest to, że nie mogę zmodyfikować 'MyObject'. –

+0

Jaka jest Twoja wartość końcowa? Prawdziwa siła odroczeń polega na tym, że możesz zarejestrować wiele wywołań zwrotnych w kolejkach wywołania zwrotnego. Zatem niezależnie od (a) funkcji synchronicznej, która została już zakończona, nadal można zarejestrować dodatkowe wywołania zwrotne. Ponadto bardzo łatwo zarejestrować wiele wywołań zwrotnych. Czy to brzmi jak coś, czego potrzebujesz? –

+0

moim prawdziwym celem jest dowiedzieć się, jak działa Deffered, próbując sprawdzić, czy można go zastosować do niektórych scenariuszy. Twoja odpowiedź jest bardzo pomocna w tym kierunku. Dziękuję Ci. –

2

Nie ma nic złego w realizacji. I jak wszyscy wiemy, używanie jQuery nie zawsze jest najlepszą metodą.

zrobiłbym to tak: (bez konieczności modyfikowania klasy 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'); 
    }); 
}; 

Jak widać, nie ma prawdziwy przewaga nad równiną JavaScript metoda. :)

Oto moje skrzypce: http://jsfiddle.net/jwa91/EbWDQ/

3

Kiedy zaprogramować, aby pamiętać, zasady lub wytyczne uchwycenia jest bardzo ważne.

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

aby uzyskać wysoki i niski Sprzęgło spójności oznacza, że ​​nasz kod będzie lepiej, bardziej wielokrotnego użytku i łatwiejszy w utrzymaniu.

Tak więc klasa MyObject nie może znać istoty kolejki. MyObject będzie znał swoje własne funkcje i metody oraz wszystko inne.

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

Główną funkcją/rozmówcy będą wiedzieć MyObject existance i stworzy trzy instancje, które będą wykonywane sekwencyjnie.

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

Mam wbudowaną wtyczkę dla jQuery: iterativeWhen. Działa z jQuery 1.8 i nowszymi wersjami.

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

Powiązane problemy