when.js很小,壓縮後只有數kb,gzip後的大小几乎能夠忽略。在Node和瀏覽器環境裏均可以使用when.jsjavascript
首先,咱們看一小段代碼:html
var getData = function(callback) { $.getJSON(api, function(data){ callback(data[0]); }); } var getImg = function(src, callback) { var img = new Image(); img.onload = function() { callback(img); }; img.src = src; } var showImg = function(img) { $(img).appendTo($('#container')); } getData(function(data) { getImg(data, function(img) { showImg(img); }); });
這段代碼完成了三個任務:1)獲取數據;2)加載圖片;3)顯示圖片,其中,任務1和2是異步,3是同步,使用的是最多見的callback機制來處理異步邏輯,好處是淺顯易懂,缺點是強耦合、不直觀、處理異常麻煩等等。java
咱們嘗試用when.js改寫下這段代碼:git
var getData = function() { var deferred = when.defer(); $.getJSON(api, function(data){ deferred.resolve(data[0]); }); return deferred.promise; } var getImg = function(src) { var deferred = when.defer(); var img = new Image(); img.onload = function() { deferred.resolve(img); }; img.src = src; return deferred.promise; } var showImg = function(img) { $(img).appendTo($('#container')); } getData() .then(getImg) .then(showImg);
看最後三行代碼,是否是一目瞭然,很是的語義化?來看下改寫後的任務一、2多了些什麼:github
var deferred = when.defer();
定義了一個deferred對象。編程
deferred.resolve(data);
在異步獲取數據完成時,把數據做爲參數,調用deferred對象的resolve方法。api
return deferred.promise;
返回了deferred對象的promise屬性。
在Promises/A規範中,每一個任務都有三種狀態:默認(pending)、完成(fulfilled)、失敗(rejected)。數組
有人可能會以爲奇怪:改變任務狀態的resolve和reject方法是定義在deferred對象上,但最後返回的倒是deferred的promise屬性。這麼作一是由於規範就是這麼定的,二是能夠防止任務狀態被外部改變。
then有三個參數,分別是onFulfilled、onRejected、onProgress,經過這三個參數,就能夠指定上一個任務在resolve、reject和notify時該如何處理。例如上一個任務被resolve(data),onFulfilled函數就會被觸發,data做爲它的參數;被reject(reason),那麼onRejected就會被觸發,收到reason。任什麼時候候,onFulfilled和onRejected都只有其一能夠被觸發,而且只觸發一次;onProgress顧名思義,每次notify時都會被調用。下面是reject和notify的用法:promise
function run() { var deferred = when.defer(); var start = 1, end = 100; (function() { if(start <= end) { deferred.notify(start++); setTimeout(arguments.callee, 50); } else { deferred.reject('time out!'); } })(); return deferred.promise; } run().then(undefined, function(reason) { alert(reason); }, function(s) { document.getElementById('output').innerHTML = s + '%'; } );
then會傳遞錯誤,也就是說有多個任務串行執行時,咱們能夠只在最後一個then定義onRejected。只定義了onRejected的then等同於otherwise,也就是說 otherwise(onRejected) 是 then(undefined, onRejected) 的簡便寫法。
then會在try..catch..的包裹之下執行任務,因此任務的異常都會被when.js捕獲,當作失敗狀態處理,相似這樣:瀏覽器
try { ... } catch (e) { deferred.reject(e); }
在任務狀態改變以後再then,依然能夠正常工做,後續任務會馬上執行。若是要在多個任務最後作cleanup工做,而無論以前的任務成功與否,能夠用ensure方法。它只接受一個參數onFulfilledOrRejected,始終會執行。另外when.js還有一個always方法,即將廢棄,建議你們不要使用。
回到上面加載圖片的場景,若是把任務2變爲:加載多張圖片,所有完成後再執行任務3。這時候須要用到when.all,when.all接受一個promise數組,返回promise,這個promise會在promise數組中每個promise都resolve以後再resolve。提及來拗口,看代碼就明白了:
var getData = function() { var deferred = when.defer(); $.getJSON(api, function(data){ var data = data.slice(0, 3); deferred.resolve(data); }); return deferred.promise; } var getImg = function(src) { var deferred = when.defer(); var img = new Image(); img.onload = function() { deferred.resolve(img); }; img.src = src; return deferred.promise; } var showImgs = function(imgs) { $(imgs).appendTo($('#container')); } var getImgs = function(data) { var deferreds = []; for(var i = 0; i < data.length; i++) { deferreds.push(getImg(data[i])); } return deferreds; } when.all(getData().then(getImgs)).then(showImgs);
若是咱們只是想把一個promise數組挨個執行一遍,能夠用when.settle:
var promise1 = function() { var deferred = when.defer(); setTimeout(function() { deferred.reject('A'); }, 2000); return deferred.promise; }; var promise2 = function() { var deferred = when.defer(); setTimeout(function() { deferred.resolve('B'); }, 2000); return deferred.promise; }; when.settle([promise1(), promise2()]).then(function(result) { console.log(result); /* [{"state":"rejected","reason":"A"}, {"state":"fulfilled","value":"B"}] */ });
有時候,咱們須要引入任務競爭機制,例如從一批cdn中找到最快的那個,when.any就派上用場了,when.any接受promise數組,在其中任何一個resolve後就接着執行後續任務了。若是要在一批promise中某幾個resolve後執行後續任務,能夠用when.some,它比when.any多一個howMany的參數。
Promise給異步編程代碼帶來了巨大的方便,今後咱們能夠更專一單個任務的實現,promise會很好的替咱們解決任務調度問題。when.js提供的功能遠遠不止本文提到的這些,有興趣的同窗能夠前往官方api文檔瞭解更多。
github:https://github.com/cujojs/when#legacy-environments
via:Jerry Qu
本文連接:https://imququ.com/post/promises-when-js.html