淺析Promise用法
本文原載於 簡書,作者飢人谷_林
關鍵詞:Promise,resolve,reject,Prepending,Resolve,Reject,then,catch,all,race
所謂Promise,簡單說就是一個容器,裡面保存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取非同步操作的消息。Promise 提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。
Promise對象有以下兩個特點。
(1)對象的狀態不受外界影響。Promise對象代表一個非同步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。有了Promise對象,就可以將非同步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的介面,使得控制非同步操作更加容易。
要理解Promise要知道沒有Promise的回調地獄:
一般我們要在一個函數執行完之後執行另一個函數我們稱之為callback『回調』,簡單的寫一下:
setTimeout(function(){n left(function(){n setTimeout(function(){n left(function(){n setTimeout(function(){n left();n },2000);n });n }, 2000);n });n}, 2000);n
以上代碼就是傳說中的回調地獄,如果有多層業務邏輯嵌套的話,不僅會使代碼閱讀困難,而且後面維護起來也是難點。
之後在ES6,Promise就應運而生。Promise語法與then的用法:
var promise = new Promise(function(resolve, reject) {n // ... some coden if (/* 非同步操作成功 */){n resolve(value);n } else {n reject(error);n }n});n
resolve(value)是在Promise在已經非同步完成成功(Resolved)之後執行的
reject(value)是在Promise在非同步失敗之後(Rejected)執行。當然,也可以用then來指定:then(resolve,reject)或者:then(resolve),catch(reject)
promise.then(function(value) {n // successn}, function(error) {n // failuren});n//等價於:npromise.then(function(){n //successn}).catch(function(){n //failuren})n
範例展示:寫一個img圖片載入示例點擊
Tips
- 連續調用回調:以剛開始的回調地獄為例子:
setTimeout(function(){nleft(function(){n setTimeout(function(){n left(function(){n setTimeout(function(){n left();n },2000);n });n }, 2000);n});n}, 2000);n//我們給left函數內容換成console.log(11);n
var p = new Promise((resolve,reject)=>{n setTimeout( resolve , 2000 )n})n.then( ()=>setTimeout( null, 2000 ) )n.then( ()=>setTimeout(function(){n console.log(11)n},2000) )n//這樣在6秒鐘之後會打出11n
範例點擊
總結:- 可以採用連續的then鏈式操作來寫回調(這是因為返回值一直是新的Promise實例)。
- 以上例子可以看出來只要在第一個promise回調中添加resolve,之後的連續then就會默認執行。
可以在then中return出數據,並且這個數據會以參數的形式傳入下一個then。
var p = new Promise(function(resolve,reject){n var a=1n resolve(a); nn}).then(function(data){n console.log(data)n return ++data;n}).then( function(data){n console.log(data)n} )n//列印出來的結果依次是: 1 2 。n
接下來介紹一下catch()
- catch是用於指定發生錯誤時的回調函數。(建議不要在then的第二個參數寫rejected狀態,總是使用catch)
- catch()使回調報錯時不會卡死js而是會繼續往下執行。點擊範例
- Promise 對象的錯誤具有「冒泡」性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。如:
getJSON(/post/1.json).then(function(post) {nreturn getJSON(post.commentURL);n}).then(function(comments) {n// some coden}).catch(function(error) {n// 處理前面三個Promise產生的錯誤n});n
用一段小代碼來理解catch()
var p = new Promise((resolve,reject)=> {n nn} ).then(()=>console.log(運行成功))n.catch( ()=>{a;console.log(報錯);} )//這裡我們沒有定義a的值會報錯n.catch( ()=> console.log(報錯2) )n.then( ()=>console.log(報錯後的回調) )n//運行結果是:報錯2 報錯後的回調n
首先n沒有定義,所以第一層出錯。下一個then的『運行成功』不會被打出來。而是會被下一個catch捕獲,第一個catch沒有定義a,所以報錯,console.log(報錯)沒辦法打出來,又被下一個catch捕獲: 第二個catch沒有問題:打出『報錯2』。運行成功傳給下一個then,打出報錯後的回調。
這裡要注意,不管是then或者catch返回的都是一個新的Promise實例!而每個Primise實例都有最原始的Pending(進行中)到Resolve(已完成),或者Pending(進行中)到Reject(已失敗)的過程。
Promise.all()
Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。
如:
var p = Promise.all([p1, p2, p3]);n
all()接受數組作為參數。p1,p2,p3都是Promise的實例對象,p要變成Resolved狀態需要p1,p2,p3狀態都是Resolved,如果p1,p2,p3至少有一個狀態是Rejected,p的狀態就變成Rejected(個人感覺很想&&符號鏈接)
Promise.race();
var p = new Promise( [p1,p2,p3] )n
上面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。(感覺就是||符號操作~~~)
Promise resolve():
有時需要將現有對象轉為Promise對象,Promise.resolve方法就起到這個作用。
Promise.resolve等價於下面的寫法。
Promise.resolve(foo)n// 等價於nnew Promise(resolve => resolve(foo))n
Promise reject()
Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態為rejected。
Promise.reject(foo)n// 等價於nnew Promise(reject => reject(foo))n
注意,Promise.reject()方法的參數,會原封不動地作為reject的理由,變成後續方法的參數。這一點與Promise.resolve方法不一致。
參考:
阮一峰ES6大白話講解Promise](http://www.cnblogs.com/lvdabao/p/es6-promise-1.html)想學更多前端知識可以加群:156856480 飢人谷大前端(8群)
(??????) ?
推薦閱讀:
※前端瀏覽器緩存及代碼部署
※Angular AOT優化構建嘗試
※解密Angular WebWorker Renderer (一)
※如何不擇手段提升scroll事件的性能