最近在看《你不知道的javascript中卷》,發覺做者花了基本一半的篇幅去講異步和promise,以爲有必要總結一下。javascript
其實本文的目的是想手寫一個Promise的,無奈總結着總結着發覺篇幅有點長,所以只好一分爲二,先介紹promise的用法,知道怎麼用,咱們才知道怎麼寫,因此把手寫一個promise的任務放到了下一篇文章當中。java
固然,網上有不少關於promise的文章,均可以參考參考,有誤之處,歡迎之處。ajax
promise是ES6新增的一個特徵,它已被列入ES6的正式規範中數組
Promise對象能夠理解爲一次執行的異步操做,使用promise對象以後可使用一種鏈式調用的方式來組織代碼;讓代碼更加的直觀。也就是說,有了Promise對象,就能夠將異步操做以同步的操做的流程表達出來,避免了層層嵌套的回調函數。promise
示例:未使用promise,回調必須層層嵌套瀏覽器
$.ajax(url1, function(data1){ // do something... $.ajax(url2, function(data2){ // do something... $.ajax(url3, function(data3){ // do something... done(data3); // 返回數據 }) }); });
若是有多個嵌套,致使代碼不夠直觀,並且若是幾個操做以前沒有先後順序之分,須要等待上一個操做完成才能夠進行下一個操做,形成沒必要要的等待異步
promise就是爲了解決這些問題而產生的。async
Promise
對象的特色:一、對象的狀態不受外界影響。函數
Promise
對象表明一個異步操做,有三種狀態測試
pending
(執行中)Resolved
(成功,又稱Fulfilled)rejected
(拒絕)其中pending爲初始狀態,fulfilled和rejected爲結束狀態(結束狀態表示promise的生命週期已結束)。
promise只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態.。
二、一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。
Promise
對象的狀態改變,只有兩種可能:從Pending
變爲Resolved
和從Pending
變爲Rejected
pending->fulfilled,pending->rejected。
只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果
Promise
對象的缺點:一、沒法取消Promise
,一旦新建它就會當即執行,沒法中途取消。
二、若是不設置回調函數,Promise
內部拋出的錯誤,不會反應到外部。
三、當處於Pending
狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。
除了IE這種古老的瀏覽器和一些低版本的安卓外,現代瀏覽器支持仍是挺好的,因此咱們能夠在谷歌的控制檯直接測試咱們的代碼
先提早說明一下,下面的代碼示例,均可以複製到谷歌的控制檯就行測試!!
一、基本用法:
(1)、首先咱們new一個Promise,將Promise實例化
(2)、而後在實例化的promise能夠傳兩個參數,一個是成功以後的resolve,一個是失敗以後的reject
(3)、Promise實例生成之後,能夠用then
方法分別指定Resolved
狀態和Reject
狀態的回調函數
代碼以下:
var promise = function(isReady){ return new Promise(function(resolve, reject){ // do somthing, maybe async if (isReady){ return resolve('hello world'); } else { return reject('failure'); } }); } //Promise實例生成之後,能夠用then方法分別指定Resolved狀態和Reject狀態的回調函數。 promise(true).then(function(value){ // success,這裏是resolve的回調函數 console.log(value); //hello world }, function(err){ // failure,這裏是reject的回調函數 console.log(err) })
上述代碼是執行成功,返回hello world,若是想測試一下失敗後的返回值,能夠在promise(true).then...這裏改成 promise(false).then...便可
二、鏈式操做
也許你會說,Promise只是簡化層層回調的寫法而已吧,其實否則,它的精髓是經過維護狀態、傳遞狀態的方式來使回調方式可以及時的調用,所以,相比於callback,它更靈活,更簡單。下面咱們來看看Promise的鏈式操做:
makePromise1() .then(function(value){ console.log(value); return makePromise2(); }) .then(function(value){ console.log(value); return makePromise3(); }) .then(function(value){ console.log(value); }); function makePromise1(){ var p = new Promise(function(resolve, reject){ //異步操做 setTimeout(function(){ console.log('異步任務1'); resolve('異步任務1傳過來的值'); }, 2000); }); return p; } function makePromise2(){ var p = new Promise(function(resolve, reject){ //異步操做 setTimeout(function(){ console.log('異步任務2'); resolve('異步任務2傳過來的值'); }, 2000); }); return p; } function makePromise3(){ var p = new Promise(function(resolve, reject){ //異步操做 setTimeout(function(){ console.log('異步任務3'); resolve('異步任務3傳過來的值'); }, 2000); }); return p; }
上面的代碼中,咱們有三個異步操做,makePromise1,makePromise2,makePromise3。其中第二個和第三個依次執行,也就是上一個操做完成以後才能夠進行。
輸出的值爲:
異步任務1
異步任務1傳過來的值
異步任務2
異步任務2傳過來的值
異步任務3
異步任務3傳過來的值
三、Promise的catch方法
var promise = function(isReady){ return new Promise(function(resolve, reject){ // do somthing, maybe async if (isReady){ return resolve('hello world'); } else { return reject('failure'); } }); } promise(true) .then(function(value){ console.log('resolved'); console.log(value); console.log(haha); //此處的haha未定義 }) .catch(function(error){ console.log('rejected'); console.log(error); });
catch 方法是 then(onFulfilled, onRejected) 方法當中 onRejected 函數的一個簡單的寫法,也就是說能夠寫成 then(fn).catch(fn),至關於 then(fn).then(null, fn)
使用 catch 的寫法比通常的寫法更加清晰明確,其實能夠類比成try/catch,這樣,其中有報錯的地方不會阻塞運行。好比定義了一個未定義haha,正常來講它上面的代碼也不會運行,由於被這個報錯阻塞了,有了catch,它上面的代碼能夠正常運行下去:
控制檯打印出來的東西:
resolved
hello world
rejected
ReferenceError: haha is not defined(…)
四、promise.all方法
Promise.all
能夠接收一個元素爲 Promise 對象的數組做爲參數,當這個數組裏面全部的 Promise 對象都變爲 resolve 時,該方法纔會返回。
代碼示例:
var p1 = new Promise(function (resolve) { setTimeout(function () { resolve("第一個promise"); }, 3000); }); var p2 = new Promise(function (resolve) { setTimeout(function () { resolve("第二個promise"); }, 1000); }); Promise.all([p1, p2]).then(function (result) { console.log(result); // ["第一個promise", "第二個promise"] });
上面的代碼中,all接收一個數組做爲參數,p1,p2是並行執行的,等兩個都執行完了,纔會進入到then,all會把全部的結果放到一個數組中返回,因此咱們打印出咱們的結果爲一個數組。
值得注意的是,雖然p2的執行順序比p1快,可是all會按照參數裏面的數組順序來返回結果。all的使用場景相似於,玩遊戲的時候,須要提早將遊戲須要的資源提早準備好,才進行頁面的初始化。
五、promise.race方法
race的中文意思爲賽跑,也就是說,看誰跑的快,跑的快的就贏了。所以,promise.race也是傳入一個數組,可是與promise.all不一樣的是,race只返回跑的快的值,也就是說result返回比較快執行的那個。
var p1 = new Promise(function (resolve) { setTimeout(function () { console.log(1); resolve("第一個promise"); }, 3000); }); var p2 = new Promise(function (resolve) { setTimeout(function () { console.log(2); resolve("第二個promise"); }, 1000); }); Promise.race([p1, p2]).then(function (result) { console.log(result); }); // 結果: // 2 // 第二個promise // 1
能夠看到,傳的值中,只有p2的返回了,可是p1沒有中止,依然有執行。
race的應用場景爲,好比咱們能夠設置爲網路請求超時。寫兩個promise,若是在必定的時間內若是成功的那個咱們沒有執行到,咱們就執行失敗的那個,這裏再也不舉例子,能夠看看阮一峯的ES入門。
ES6的介紹就到這裏了,下一篇文章咱們來手寫一個promise