標籤:out 參數 接收 逆向 第一個 log 意思 開發 deferred
前段時間的工作中,由於項目要在前端實現儲存,於是便使用了websql,而websql的API涉及到了很多的非同步問題,如果採取回呼函數的方式處理,代碼不夠優雅,而且不利於理解,於是便找到了Promise,使用之後有一些自己的理解和心得,跟大家在本文中一起分享一下。
Promise為何物?
Promise中文釋義為“誓言”、“承諾”之意,根據其音譯,那就是“普羅米修斯”,這貨很強大啊,在希臘神話中,是最具智慧的神明之一,最早的泰坦巨神後代,名字有“先見之明”(Forethought)的意思。
而JS中的Promise,其實就是ES6提供的一個對象,它扮演的就是“Crowdsourced Security Testing”這麼個角色,它用來傳遞非同步作業的訊息,代表了某個未來才會知道結果的事件,並且這個事件提供統一的API,便於進一步處理,這個類目前在chrome32、Opera19、Firefox29以上的版本都已經支援。
接下來說說Promise規範,大家應該都不陌生了,因為這個規範已經出來很長一段時間了,目前在chrome32、Opera19、Firefox29以上的版本都已經支援Promise了,Promise規範的主要內容如下:
- Promise對象有三種狀態:Pending(進行中)、Resolved(已完成)、Rejected(已失敗),這三種狀態只能從Pending轉到Rejected或者Pending轉到Rejected,不能逆向轉換,而且狀態一旦轉換完成後,狀態就凝固了,不能夠再改變了。只有非同步作業的結果,才能決定當前是哪一種狀態,任何其他動作都無法改變這個狀態,這點跟Deferred是不一樣的。
- Promise模式唯一需要的一個介面是調用then方法,它可以用來註冊當promise完成或者失敗時調用的回呼函數,then方法接受兩個參數,第一個參數是成功時的回調,在promise由Pending態轉換到Resolved態時調用,另一個是失敗時的回調,在promise由Pending態轉換到Rejected態時調用。
Promise有啥好處?
說了這麼多,Promise到底有啥好處呢?能為我們的工作帶來什麼便捷呢?話不多說,是騾子是馬拉出來遛遛,我們先new一個來玩玩:
var p = new Promise(function(resolve, reject){ //用setTimeout類比非同步作業 setTimeout(function(){ console.log(‘第一個Promise執行完成‘); resolve(‘第一個Promise‘); }, 2000);});
上面的代碼可以看出,new一個Promise對象時,要傳入一個函數,而這個函數一般有兩個參數,一個是resolve,另一個是reject,這兩個參數不一定都要,但你要用的那個一定要傳入,所以兩個都寫上就好了,這兩個參數其實代表的是非同步作業執行成功後的回呼函數和非同步作業執行失敗後的回呼函數。而上面這段代碼的意思就是用setTimeout模仿一個非同步操作,2秒後,在控制台列印“第一個Promise執行完成”,然後調用resolve方法,帶一個參數:“第一個Promise”,運行代碼,會直接輸出“第一個Promise執行完成”,雖然我們只是new了一個Promise對象,但是函數當參數傳進去的時候會被立即執行,所以直接輸出,為了防止這種情況,我們一個將這段程式碼封裝在一個函數中:
function test1(){ var p = new Promise(function(resolve, reject){ //做一些非同步作業 setTimeout(function(){ console.log(‘第一個Promise執行完成‘); resolve(‘第一個Promise‘); }, 2000); }); return p;}
如上,最後返回了一個Promise對象,接下來重頭戲來了,Promise對象的then方法要出場了,它可以用來註冊當Promise完成或者失敗時調用的回呼函數,我們接著上面的代碼繼續往下寫:
test1().then(function(val){ console.log(val); //後續操作 })
執行這段代碼,兩秒後,會在頁面輸出“第一個Promise執行完成”,緊接著輸出“第一個Promise”。到這裡,機智的你已經看出來,這個then不就是非同步處理完成後,將傳遞過來的資料取到,然後進行後續操作,就是充當一個回呼函數嘛,感覺這個Promise沒有什麼大不了的地方啊,只不過是把原來的回調寫法分離出來,在非同步作業執行完後,用鏈式調用的方式執行回呼函數而已,其實這種鏈式調用在一些比較複雜的頁面中是很有必要的,因為回呼函數如果比較少還好說,一旦多了起來而且必須講究執行順序的話,回呼函數開始嵌套,那代碼的噁心程度是簡直無法忍受。而Promise的出現就是為瞭解決這個問題的。
Promise就提供了一種優雅的解決方案,主要用法就是將各個非同步作業封裝成好多Promise,而一個Promise只處理一個非同步邏輯。最後將各個Promise用鏈式調用寫法串聯,在這樣處理下,如果非同步邏輯之間前後關係很重的話,你也不需要層層嵌套,只需要把每個非同步邏輯封裝成Promise鏈式調用就可以了。
Promise的基本API
話不多說,先用console.dir(Promise)列印出來看看:
從可以看出來主要有以下幾個方法:
- Resolve:該方法可以使 Promise 對象的狀態改變成成功,同時傳遞一個參數用於後續成功後的操作。
- Reject:該方法則是將 Promise 對象的狀態改變為失敗,同時將錯誤的資訊傳遞到後續錯誤處理的操作。
- Then: 所有的 Promise 對象執行個體裡都有一個 then 方法,它是用來跟這個 Promise 進行互動的,then方法主要傳入兩個方法作為參數,一個 resolve 函數,一個 reject 函數,鏈式調用 ,上一個Promise對象變為resolved的時候,調用then中的Resolve方法,否則調用Reject方法,且then 方法會預設調用 resolve() 函數。
- Catch:該方法是 then(onFulfilled, onRejected) 方法當中 onRejected 函數的一個簡單的寫法,也就是說也可以寫成then,但是用來捕獲異常時,用catch更加便於理解。
- All:該方法可以接收一個元素為 Promise 對象的數組作為參數,當這個數組裡面所有的 Promise 對象都變為 resolve 時,該方法才會返回。就是全部都執行完了才接著往下執行,例如:
var p1 = new Promise(function (resolve) { setTimeout(function () { resolve("p1"); }, 2000);});var p2 = new Promise(function (resolve) { setTimeout(function () { resolve("p2"); }, 2000);});Promise.all([p1, p2]).then(function (result) { console.log(result); });
6. Race:競速,類似All方法,它同樣接收一個數組,不同的是只要該數組中的 Promise 對象的狀態發生變化(無論是 resolve 還是 reject)該方法都會返回。就是只要某一個執行完了就接著往下執行。
Promise的鏈式調用
Then的鏈式調用才是Promise的意義所在,而要實現鏈式調用,在then中的resolve方法如何return很關鍵。在then方法中通常傳遞兩個參數,一個 resolve 函數,一個 reject 函數。reject暫時不討論,就是出錯的時候啟動並執行函數罷了。resolve 函數必須返回一個值才能把鏈式調用進行下去,而且這個值返回什麼是有很大講究的。
function test1(){ var p = new Promise(function(resolve, reject){ //做一些非同步作業 setTimeout(function(){ console.log(‘第一個Promise執行完成‘); resolve(‘第一個Promise傳來的值‘); }, 2000); }); return p;}test1().then(function(val){ console.log(‘進入第一個then‘); console.log(val); var p1 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log(‘第一個then執行完成‘); resolve(‘第二個Promise傳來的值‘); }, 2000); }) return p1; }).then(function(val){ console.log(‘進入第二個then‘); console.log(val);})
列印結果如下:
上述例子通過鏈式調用的方式,按順序列印出了相應的內容。then 可以使用鏈式調用的寫法原因在於,每一次執行該方法時總是會返回一個 Promise 對象,後一個then中的resolve方法要等到前一個then處理完了才執行。而返回一個新Promise之後再調用的then就是新Promise中的邏輯了。另外,在 then onFulfilled 的函數當中的傳回值,可以作為後續操作的參數。
function test1(){ var p = new Promise(function(resolve, reject){ //做一些非同步作業 setTimeout(function(){ console.log(‘第一個Promise執行完成‘); resolve(‘第一個Promise傳來的值‘); }, 2000); }); return p;}test1().then(function(val){ console.log(‘進入第一個then‘); console.log(val); setTimeout(function(){ console.log(‘第一個then執行完成‘); }, 2000); return ‘第二個Promise傳來的值‘; }).then(function(val){ console.log(‘進入第二個then‘); console.log(val);})
列印結果如下:
可以看出,第一個then中的resolve還未執行完,就進入了第二個then中,沒有進行等待,是因為第一個then中返回的是一個字串,而不是Promise對象,返回的值會傳遞到下一個then的resolve方法參數中。
總結
以上就是ES6 Promise的基本概念和用法,Promise還是非常贊的,如果你還在使用回調模式,我強烈建議你切換到 Promise,這樣你的代碼會變的更少,更優雅,並且更加容易理解,這樣就不會等你開發完後,被那些讀你代碼的人在背後偷偷罵你。本文我自己理解結合網上的一些資料,希望你看完之後對你有協助,有不足之處歡迎指正,謝謝~
大話JS神器之Promise