謝謝n͛i͛g͛h͛t͛i͛r͛e͛
大大指出的關於Promise
中catch
用的不到位的錯誤,貼上大大推薦的文章Promise中的菜鳥和高階錯誤,文章很詳細說明了一些Promise
使用中的錯誤和指導。另外更正內容在後面補充。javascript
說到異步流程控制,以前用的比較多的是jQ的Deferred。那Deferred是個啥呢,不清楚不要緊,直接控制檯來打印看下:html
喔!看得出$.Deferred()後是個對象,其下面有着熟悉的done
, fail
, always
字眼(對,,是否是有點熟悉了呢?沒錯!若是常常用ajax的話就會常常接觸到這些貨色)。 固然了,不止這些,還有最最最重要的reject
和resolve
方法,說到這兩個方法,就得引出下Deferred的狀態機制了——其實很簡單,實例化後用上圖中的state
方法就能夠查看($.Deferred().state()
),有三種狀態java
執行resolve/reject前,返回值是pendingajax
執行了resolve,返回值是resolvedsegmentfault
執行了reject,返回值是rejectedpromise
直接來試着用下吧!這裏咱們假設執行一個隨機延時的setTimeout
的異步操做,在setTimeout
異步操做結束後,根據延時大小,作出不一樣迴應 ! 代碼:dom
function log (msg) { console.log(msg); } // 包裝一個異步操做 var Async = function () { // 生成一個0到5秒的延遲 var delay = Math.floor(Math.random() * 5); // 建立一個Deffered對象 var dfd = $.Deferred(); // 這裏調用一個異步操做 setTimeout(function(){ if (delay <= 2) { // 置dfd狀態爲resolved dfd.resolve('一切正常!'); } else { // 置dfd狀態爲rejected dfd.reject('超時了!'); } }, delay * 1000) // 這裏要返回Deferred下的promise對象Dererred對象的緣由下面會解釋 return dfd.promise(); } Async() .done(function (data) { log(data) // 若是延遲不大於三秒 輸出dfd.resolve()中的數據 '一切正常!' }) .fail(function (err) { log(err) // 反之則 輸出dfd.reject()中的數據 '超時了!' }) .always(function () { log('執行完畢!'); // 老是輸出 '執行完畢!' })
在某個操做開始前建立一個
Deferred
對象,而後執行操做異步操做間可根據狀況給dfd執行
relove
或者reject
方法改變狀態並傳入數據函數最後返回出dfd的對象下的一個promise對象,這裏不直接返回dfd對象是由於dfd對象的狀態是在第一次resolve或者reject後還能夠更改的(不過裏面的數據以第一次爲準)!!spa
操做執行後用
done
和fail
方法分別接受resolve和reject狀態和數據(一一對應)而後執行回調(其實1.8還有個then
方法,接受兩個參數,第一個參數爲resolve
的回調,第二個爲reject
的)
always
是不管resolve
仍是reject
都會執行。
我是一個流水線車間質檢工人,就在日常的這樣的一天,來了一批玩具熊,嗯,接下來應該是這樣的
來了一個檢查目標(
$.Dererred()
),這時你還不知道它是好是壞我靠我幾十年的新東方炒菜技巧檢驗產品並給良品貼上了合格標籤(
dfd.res* olve(合格標籤)
),次品貼上回廠標籤* (dfd.reject(回廠標籤及緣由)
)而後經過的良品和次品都來到了各自的包裝口打好包,不能對裏面的標籤作更改了!(
dfd.promise()
)去往本身下一個目的地(return dfd.promise
)再而後良品來到了熊孩子手中(
.done()
),次品回到了廠裏(.fail()
),最後無論玩具熊到了哪裏,其實都會被開膛破肚(.always()
好吧這裏有點牽強)
還有值得說一下的是always
裏的回調,我在實際中使用時發現老是在done
和fail
裏的回調(假設爲同步)執行完畢後後執行的。
和上面同樣,先打印一下!
能夠看到Promise下也有熟悉的resolve
和reject
方法,好像和jQ的Deferred
頗爲類似!可是不是少了點什麼呢?done
或者fail
之類的流程控制的方法呢??
不急,其實展開prototype
原型上就能夠看到掛載着的then
方法了!(像極了jQ1.8後那個then
,不過我以爲應該說是jQ來遵循Promise
纔對)
Promise其實就是個構造函數,仍是以前的例子,這裏咱們分三步走
var Async = function () { // 第一步,新建個promise對象,所需的異步操做在其中進行 var prms = new Promise(function(resolve, reject){ // 生成一個0到5秒的延遲 var delay = Math.floor(Math.random() * 5); // 這裏調用一個異步操做 setTimeout(function(){ // 第二步, 根據狀況置promise爲resolve或者reject if (delay <= 2) { // 置dfd狀態爲resolved resolve('一切正常!'); } else { // 置dfd狀態爲rejected reject('超時了!'); } }, delay * 1000) }) // 第三步,返回這個Promise對象 return prms } // 強大的來了 Async() // then接受兩個函數分別處理resolve和reject兩種狀態 .then( function(data) { console.log(data) // 一切正常! }, function(err) { console.log(err) // 超時了!! })
粗粗一看好像和Dererred
不能更像了,,不過細心點的話能夠發現咱們在函數裏直接返回了prms
這個對象,而不是像以前把包裝了一層。。。對!由於Promise
的特性就是一旦第一次賦予了狀態後面就沒法更改了,這也算省心多了吧。可是問題來了,我爲何要選擇用Promise
呢??
這麼說吧,它是原生的 它是原生的 它是原生的!,還有能夠鏈式鏈式鏈式鏈式調用!,咱們能夠把每個then
或者catch
當作一個處理器, 好比這樣
Async() // 這裏暫時只處理resolve .then(function(data) { console.log(data) // 一切正常! return Promise.resolve('隨便什麼'); }) // 下一個then處理器接收到上一個處理器發出的數據 .then(function(data2) { console.log(data2) // 隨便什麼 return Promise.reject('錯誤數據'); }) ...
對!沒看錯,其實在then
裏面你還能夠return
其餘的promise
對象傳並遞數據!更有甚你甚至能夠什麼都不返回,好比說這樣
Async() .then(function(data) { console.log(data) // 一切正常! }) // 上面那個處理器若是不return任何東西 就會默認返回個resolve(undefined) // 而後下面的處理器就會接收到這個resolve(undefined) .then(function(data2) { console.log(data2) // undefined // 雖然沒有數據來處理,可是你還能夠在這裏作一些事情啊,例如 return Promise.reject('錯誤數據'); }) // 嗒噠,catch就這麼登場了,這裏用catch處理上個then處理器發出的reject .catch(fucntion(err){ console.log(err) // 錯誤數據 return '那直接返回個字符串呢?' }) // 上個catch處理器返回了個字符串其實也會被下個處理器接受 // 至關於resolve('那直接返回個字符串呢?') .then(function(data3){ console.log(data3) // 那直接返回個字符串呢? }) // 好,接着咱們來試試在沒有返回任何東西的狀況下接一個catch處理器 .catch(function(err2){ console.log(err2) // 咱們能夠來猜一下上面會輸出什麼,undefined嗎? // 錯,其實這裏什麼都不會輸出,由於這個catch接收的是resolve // 但它並不會吞沒這個resolve而是選擇跳過,例如咱們這裏再返回 return Promise.resolve('這個字符串會被跳過') }) // 這裏緊接着個then處理器,它接受到的數據呢 // 其實並非上個catch返回的resolve('這個字符串會被跳過') // 而是catch以前那個then處理器默認返回的resolve(undefined) .then(function(data4){ console.log(data4) // undefined })
有點被繞暈了吧
鏈式調下會有一串then
和catch
,這些then
和catch
處理器會按照順序接受上個處理器所產生的返回值,而且根據傳入的狀態作出不一樣響應,要麼跳過,要麼處理(因此上面23行處的catch
處理器被跳過了)
ps: 上面咱們用的then
處理器只有一個函數參數,因此只會處理resolve
狀態,若是是兩個then
就能夠處理reject
了。
----更新於5月11日-----
catch
使用的注意上面一塊代碼中引出了catch
處理器, 以前覺得 cacth()
是 then(null, ...)
的語法糖, 其實這麼說不徹底正確(功能層面上來講這兩個是徹底相同的沒錯——都是處理reject
和異常),可是到了實際使用中Promise中的菜鳥和高階錯誤文章中給出了明確的狀況證實,這裏貼一下:
首先只處理異常狀況,下面兩個是等價的
somePromise().catch(function (err) { // 處理異常 }); somePromise().then(null, function (err) { // 處理異常 });
可是,若是不僅是處理異常的下面兩種狀況下就不同了
somePromise().then(function () { return otherPromise(); }).catch(function (err) { // 處理異常 }); somePromise().then(function () { return otherPromise(); }, function (err) { // 處理異常 });
不夠清楚嗎?那麼若是是這樣呢?若是第一個回調函數拋出一個錯誤
會發生什麼?
somePromise().then(function () { throw new Error('這裏錯了!'); }).catch(function (err) { console.log(err) // 這裏錯了! :) }); somePromise().then( function () { throw new Error('這裏錯了'); }, function (err) { console.log(err) // 未知 :( // 並無catch到上面那個Error });
結論就是,當使用 then(resolveHandler, rejectHandler)
, rejectHandler
不會捕獲在 resolveHandler
中拋出的錯誤!
看似這個注意項並不影響日常使用,原文做者也說道:
由於,筆者的我的習慣是從不使用then方法的第二個參數,轉而使用 catch() 方法
那麼,問題來了,如何正確的使用catch
呢? 其實我沒有很好的想明白,但願指教
,隨便拋兩個磚
// 1 somePromise() .then(resolveHandler) // 這個catch會處理somePromise或者resolveHandler的異常 .catch(rejectHandler) .then(otherResolveHandler) // 而這個catch呢只會處理resolveHandler的異常 .catch(otherRejectHandler) // 2 somePromise() .then(resolveHandler) .then(otherResolveHandler) // 至於這個catch則會處理somePromise、resolveHandler和otherResolveHandler的異常 .catch(rejectHandler) // 3 somePromise() .catch(console.log.bind(console)) //等價於 .catch(function(err){ console.log(err) })
哈哈哈哈哈哈,仍是好好再去想一想Promise去了,弄明白了再來補充,再次謝謝@n͛i͛g͛h͛t͛i͛r͛e͛大大,荊柯刺秦王
寫的很粗糙,有錯誤的地方但願多多指教!!