本篇,主要普及promise的用法。html
一直以來,JavaScript處理異步都是以callback的方式,在前端開發領域callback機制幾乎深刻人心。在設計API的時候,不論是瀏覽器廠商仍是SDK開發商亦或是各類類庫的做者,基本上都已經遵循着callback的套路。前端
近幾年隨着JavaScript開發模式的逐漸成熟,CommonJS規範順勢而生,其中就包括提出了Promise規範,Promise徹底改變了js異步編程的寫法,讓異步編程變得十分的易於理解。html5
在callback的模型裏邊,咱們假設須要執行一個異步隊列,代碼看起來可能像這樣:node
loadImg('a.jpg', function() { loadImg('b.jpg', function() { loadImg('c.jpg', function() { console.log('all done!'); }); }); });
這也就是咱們常說的回調金字塔,當異步的任務不少的時候,維護大量的callback將是一場災難。當今Node.js大熱,好像不少團隊都要用它來作點東西以沾沾「洋氣」,曾經跟一個運維的同窗聊天,他們也是打算使用Node.js作一些事情,但是一想到js的層層回調就望而卻步。git
好,扯淡完畢,下面進入正題。es6
Promise可能你們都不陌生,由於Promise規範已經出來好一段時間了,同時Promise也已經歸入了ES6,並且高版本的chrome、firefox瀏覽器都已經原生實現了Promise,只不過和現現在流行的類Promise類庫相比少些API。github
所謂Promise,字面上能夠理解爲「承諾」,就是說A調用B,B返回一個「承諾」給A,而後A就能夠在寫計劃的時候這麼寫:當B返回結果給個人時候,A執行方案S1,反之若是B由於什麼緣由沒有給到A想要的結果,那麼A執行應急方案S2,這樣一來,全部的潛在風險都在A的可控範圍以內了。ajax
上面這句話,翻譯成代碼相似:chrome
var resB = B(); var runA = function() { resB.then(execS1, execS2); }; runA();
只看上面這行代碼,好像看不出什麼特別之處。但現實狀況可能比這個複雜許多,A要完成一件事,可能要依賴不止B一我的的響應,可能須要同時向多我的詢問,當收到全部的應答以後再執行下一步的方案。最終翻譯成代碼可能像這樣:編程
var resB = B(); var resC = C(); ... var runA = function() { reqB .then(resC, execS2) .then(resD, execS3) .then(resE, execS4) ... .then(execS1); }; runA();
在這裏,當每個被詢問者作出不符合預期的應答時都用了不一樣的處理機制。事實上,Promise規範沒有要求這樣作,你甚至能夠不作任何的處理(即不傳入then的第二個參數)或者統一處理。
好了,下面咱們來認識下Promise/A+規範:
then
方法(能夠說,then就是promise的核心),並且then必須返回一個promise,同一個promise的then能夠調用屢次,而且回調的執行順序跟它們被定義時的順序一致能夠看到,Promise規範的內容並不算多,你們能夠試着本身實現如下Promise。
如下是筆者本身在參考許多類Promise庫以後簡單實現的一個Promise,代碼請移步promiseA。
簡單分析下思路:
構造函數Promise接受一個函數resolver
,能夠理解爲傳入一個異步任務,resolver接受兩個參數,一個是成功時的回調,一個是失敗時的回調,這兩參數和經過then傳入的參數是對等的。
其次是then的實現,因爲Promise要求then必須返回一個promise,因此在then調用的時候會新生成一個promise,掛在當前promise的_next
上,同一個promise屢次調用都只會返回以前生成的_next
。
因爲then方法接受的兩個參數都是可選的,並且類型也沒限制,能夠是函數,也能夠是一個具體的值,還能夠是另外一個promise。下面是then的具體實現:
Promise.prototype.then = function(resolve, reject) { var next = this._next || (this._next = Promise()); var status = this.status; var x; if('pending' === status) { isFn(resolve) && this._resolves.push(resolve); isFn(reject) && this._rejects.push(reject); return next; } if('resolved' === status) { if(!isFn(resolve)) { next.resolve(resolve); } else { try { x = resolve(this.value); resolveX(next, x); } catch(e) { this.reject(e); } } return next; } if('rejected' === status) { if(!isFn(reject)) { next.reject(reject); } else { try { x = reject(this.reason); resolveX(next, x); } catch(e) { this.reject(e); } } return next;