若是你看了不少的Promise的文檔和資源,可是卻發現本身老是掌握不了Promise的精髓,也許你能夠看看個人的這篇文章,或許對你有所幫助。git
在學習使用Promise以前,須要對JS的運行機制有所瞭解。es6
JavaScript的併發模型基於"事件循環",這個模型與像 C 或者 Java 這種其它語言中的模型大相徑庭。github
函數調用造成了一個棧幀。segmentfault
function foo(b) { var a = 10; return a + b + 11; } function bar(x) { var y = 3; return foo(x * y); } console.log(bar(7));
當調用bar時,建立了第一個幀 ,幀中包含了bar的參數和局部變量。當bar調用foo時,第二個幀就被建立,並被壓到第一個幀之上,幀中包含了foo的參數和局部變量。當foo返回時,最上層的幀就被彈出棧(剩下bar函數的調用幀 )。當bar返回的時候,棧就空了。promise
對象被分配在一個堆中,即用以表示一個大部分非結構化的內存區域。併發
一個 JavaScript 運行時包含了一個待處理的消息隊列。每個消息都與一個函數相關聯。當棧擁有足夠內存時,從隊列中取出一個消息進行處理。這個處理過程包含了調用與這個消息相關聯的函數(以及於是建立了一個初始堆棧幀)。當棧再次爲空的時候,也就意味着消息處理結束。異步
之因此稱爲事件循環,是由於它常常被用於相似以下的方式來實現:函數
while (queue.waitForMessage()) { queue.processNextMessage(); }
若是當前沒有任何消息queue.waitForMessage 會等待同步消息到達。 oop
參考資料: 併發模型與事件循環學習
Promise實際上是一個函數,它的實例是一個不可逆的狀態機。一般可使用下面這幾種方式去建立一個Promise的實例。
const promise = new Promise((resolve, reject) => { ...... resolve(xxx) ...... });
Promise.resolve
, Promise.reject
, Promise.all
, Promise.race
等提供的靜態方法,執行完畢後,也會返回一個Promise。
const promise = Promise.resolve('complete'); // promise instanceof Promise => true
當咱們在一個Promise的實例中,使用then, catch, finally添加完回調函數也會返回一個Promise。
const promise = Promise.resolve('complete').then((val)=> { console.log(val) // complete }) // promise instanceof Promise => true
當Promise的實例狀態由 pending => fulfilled, 會觸發then中註冊的第一個函數。
當Promise的實例狀態由 pending => rejected, 會觸發then中註冊的第二函數或者catch中註冊的函數。
當Promise的實例狀態發生變化後,finally註冊的函數都會觸發。
瞭解Promise的運行機制,可以幫助咱們更好的使用Promise。下面這張圖是根據 es6-promise 的實現而描繪的Promise的運行機制。
前面已經給你們介紹了JS的運行機制,JS是基於事件循環,當JS運行的消息隊列被清空後,將會去異步隊列中獲取消息,加入JS的運行隊列。 而在有了Promise之後, 將會在獲取異步隊列消息以前,會把Promise的運行隊列所有添加到JS運行消息隊列。在此運行期間, 若是Promise運行隊列又添加了新的回調函數, Promise運行隊列又會從新添加到JS運行的消息隊列中, 直到Promise的運行隊列和JS運行的消息隊列都清空,纔會去異步隊列中獲取消息。這樣就能保證Promise的回調函數都在異步調用以前。可是開發時候也須要防止進入Promise的回調陷阱,就像下面這樣
function bar() { Promise.resolve('resolve').then((val) => { console.log(val); bar(); }) } bar() console.log('continue'); setTimeout(() => {console.log('setTimeout')}); // continue resolve resolve resolve resolve ......
上面的例子中setTimeout將永遠不會被輸出。
Promise實例的任何狀態(pending, resolved, rejected)都能使用then, catch, finally來註冊(添加)回調函數。而不一樣的是,在pending狀態的Promise實例會把這些回調函數存儲在內部,等到狀態發生改變的時候,再把的相關回調函數所有推送promise的運行隊列。而處在resolved與rejected狀態的實例,將會當即把相關函數推送到Promise的運行隊列,而不須要等待。
var callback; var promise = new Promise((resolve) => { callback = resolve; }) promise.then(() => {console.log('1')}); Promise.resolve('2').then((val) => {console.log(val)}); callback(); // 輸出 2 1
上面例子中的promise
將會在callback執行以後,纔會把所註冊的函數推入Promise的運行隊列,因此致使所註冊的函數運行在後面。
Promise的實例狀態爲resolved時。只會把then中resolve和finally所註冊函數添加到Promise的運行隊列。並且它們的執行順序只與它們的添加順序有關。
var promise = new Promise((resolve, reject) => { resolve('ok'); }) promise.finally(() => { console.log('finally'); }); promise.then((val) => { console.log(val); }); promise.catch(() => { console.log('catch'); }); // finally ok
很明顯 finally 出如今 ok 以前,catch所註冊的函數也將不會被推送到Promise的運行隊列,也將不會被執行。
Promise的實例狀態爲rejected的時。會把then中reject和catch與finally所註冊函數添加到Promise的運行隊列。並且它們的執行順序只與它們的添加順序有關。
var promise = Promise.reject('reject'); promise.finally(() => {console.log('finally')}); promise.catch(() => {console.log('catch')}); promise.then(() => {console.log('ok')}, (val) => {console.log(val)}); // finally catch reject
上面就是我的關於Promise的探究與學習,與此相輔的還有另一篇,深刻學習Promise調用鏈。