2011-10-12 35 views
14

我有一個標準的JavaScript對象,其原型被擴展爲.start()方法,以2個回調爲參數:successfailure。此方法執行一些異步處理(它是而不是 AJAX),並根據此處理的結果調用成功或失敗回調。如何使用jQuery的延期對象與自定義javascript對象?

下面是如何這可能是系統化:

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

這不是很重要的方法內部進行準確的處理,只知道它是異步非阻塞的。當啓動方法完成處理時,我無法控制時間點。我也無法控制這種方法的原型和實現。

我控制的是successfailure回調。提供它們取決於我。

現在我有那些對象的數組:

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

此數組中元素的順序是重要的。我需要在數組的每個元素上連續觸發.start()方法,但只有在前一個完成時(即調用成功回調)纔會觸發該方法。如果發生錯誤(調用失敗回調函數),我想停止執行,不再調用數組的其餘元素上的.start方法。

我可以通過使用遞歸函數天真地實現這一點:

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

這工作得很好,但看着那是在jQuery的1.5推出我認爲還有改進這段代碼的房間jQuery's deferred Object。不幸的是,我對此感到不太舒服,我正在努力學習它。

所以我的問題是可以適應我的樸素代碼,並利用這個新的API,如果是的話,你能提供給我一些指針嗎?

這裏是一個jsfiddle與我的實施。

+0

+1總是很好看頂回答者問一個很好的問題。自我回答這個? –

回答

4

你可以做這樣的事情:(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); 
     })); 
}); 

不太清楚閹你會認爲這是一種進步,但。

+0

看起來很有趣,唯一的問題是我無法修改'MyObject'。 –

+0

順便問一下你的目標是什麼?延遲的真正威力在於您可以將多個回調註冊到回調隊列中。所以不管(a)同步函數已經完成,你仍然可以註冊額外的回調函數。此外,註冊多個回調很容易。這聽起來像是你需要的東西嗎? –

+0

我的真正目標是通過嘗試查看它是否可以應用於某些場景來學習Deffered如何工作。你的回答對這個方向非常有幫助。謝謝。 –

2

您的實施沒有任何問題。我們都知道,使用jQuery並不總是最好的方法。

我會做這樣的:(無需修改爲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'); 
    }); 
}; 

正如你所看到的,有在普通的JavaScript方法沒有真正的優勢。 :)

這裏是我的小提琴:http://jsfiddle.net/jwa91/EbWDQ/

3

當我們編程,記住GRASP原則或準則是非常重要的。

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

獲得高內聚,低耦合意味着我們的代碼會更好,更可重複使用,更容易維護。

因此,類MyObject一定不知道隊列存在。 MyObject將會知道它自己的特性和方法等等。

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

main/caller函數將知道MyObject的存在,它將創建三個它們將按順序執行的實例。

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

我爲jQuery構建了一個插件:iterativeWhen。它適用於jQuery 1.8及更高版本。

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