回调地狱解决方案之Promise
為什么出現Promise
在javascript開發過程中,代碼是單線程執行的,同步操作,彼此之間不會等待,這可以說是它的優勢,但是也有它的弊端,如一些網絡操作,瀏覽器事件,文件等操作等,都必須異步執行,針對這些情況,起初的操作都是使用回調函數實現。
實現方式如下(偽代碼):
function One(callback) {if (success) {callback(err, result);} else {callback(err, null);} } 復制代碼One(function (err, result) { //執行完One函數內的內容,成功的結果回調回來向下執行 }) 復制代碼
上述代碼只是一層級回調,如果代碼復雜后,會出現多層級的回調,代碼可讀性也會很差,那有沒有一種方式,不用考慮里面的內容,直接根據結果成功還是失敗執行下面的代碼呢?有的,Promise(承諾),在ES6中對Promise進行了同意的規范。
Promise的含義
-
書上這么說:
Promise 是異步編程的一種解決方案,比傳統的解決方案–回調函數和事件--更合理和更強大。它由社區最早提出和實現,ES6將其寫進了語言標準,統一了語法,原生提供了Promise
所謂Promise ,簡單說就是一個容器,里面保存著某個未來才回結束的事件(通常是一個異步操作)的結果。從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。 Promise 對象的狀態不受外界影響
-
Promise/a+ 官方網站給出的定義 A promise represents the eventual result of an asyncchronous operation
翻譯:表示一個異步操作的最終結果。
-
我的理解:
Promise是回調函數可以規范的鏈式調用
Promise原理與講解
原理
- pending:進行中
- fulfilled :執行成功
- rejected :執行失敗
注意Promise在某一時刻只能處于一種狀態
- pending------》fulfilled(resolved)
- pending------》rejected
Promise的狀態改變,狀態只能由pending轉換為rejected或者rejected,一旦狀態改變完成后將無法改變(不可逆性)
用代碼講原理
創建Promise需要用到Promise的構造函數來實現,代碼如下:
var promise=new Promise(function(resolve,reject){// ...some async codeif(/* 一些異步操作成功*/) { resolve(value); }else { reject(error); }
復制代碼}) 復制代碼
代碼分析:
- 在異步操作完成之后,會針對不同的返回結果調用resolve和reject。
- resolve和reject是兩個函數,resolve是異步操作成功時候被調用,將異步操作的返回值作為參數傳遞到外部;reject是異步操作出異常時候被調用,將錯誤信息作為參數傳遞出去。
- ==Promise其實沒有做任何實質的代碼操作,它只是對異步操作回調函數的不同結果定義了不同狀態。==
- resolve函數和reject函數只是把異步結果傳遞出去
},function(error){
復制代碼}); 復制代碼
代碼分析:
- then方法將兩個匿名函數作為參數,接收resolve和reject這兩個函數的值。
- value是執行成功的值,error是執行出錯時的錯誤信息。
- 對于error錯誤異常結果出現的時候,可以不單獨寫匿名錯誤的函數,可以直接用catch拋出
- 注意then方法==只是==用來獲取異步操作的值。
代碼分析:
- 上面的第二個then方法中的值雖然是未定義,但是每一個then一定會==返回一個新的peomise對==象,但是默認是一個空對象。
- 對于這個空對象我們如果想繼續做一些什么,需要進行處理,可以用非空Promise對這個空的進行賦值覆蓋,然后繼續then的鏈式調用。
- then 中的==retuen==關鍵字很重要,聯系著下一個then的調用。
幾個常用api
- Promise.resolve resolve方法用來將一個非Promise對象轉化為Promise對象
轉換的對象是一個常量或者不具備狀態的語句,轉換后的對象自動處于resolve狀態。 轉換的后的結果和原來一樣
var promise =Promise.resolve("hello world"); promise.then(function(result){console.log(result); //輸出結果 hello world }) 復制代碼轉換的對象如果直接是一個異步方法,不可以這么使用。
- Promise.all(常用api) 多個promise需要執行的時候,可以使用promise.all方法統一聲明,該方法可以將多個Promise對象包裝成一個Promise。
代碼如下
promise.all( //一系列promise操作 ).then(function(results){}).catch(function(error){
復制代碼}); 復制代碼
代碼分析:
- promise.all對多有執行結果做一個包裝傳給了then
- promise.all中的執行順序是怎么樣的,Promise的執行順序是從被創建開始的,也就是在調用all的時候,==所有的promise都已經開始執行==了,all方法只是等到==所有的對象都執行完成==,才會吧結果==傳遞給then==。
- all中的promise,如果有一個狀態變成了reject那么轉換后的Promise字節變成reject,錯誤信息傳遞哥catch,不會傳遞給then。(但是并不是說all這里面剛開始執行成功的操作就不算數了)
Promise在開發中的應用
項目開發中promise的應用代碼:
Promise.all([self.count({phoneNumber: mobile, createdOn: {$gt: hour}}),self.count({ip: ip, createdOn: {$gt: hour}})]).then(function (results) {if (results[0] >= 5) {return callback({code: -1, message: '短信發送頻率過快,每手機號1小時內只能發送5次'});}if (results[1] >= 5) {return callback({code: -1, message: '短信發送頻率過快,每IP1小時內只能發送5次'});}let code = {phoneNumber: mobile,code: tool.makeRandomStr(4, 1).toLowerCase(),createdOn: new Date(),expiredOn: new Date(new Date().getTime() + (20 * 60 * 1000)), //20分鐘失效ip: ip,isUsed: false};self.create(code, function (err, newCode) {if (newCode) {sms.sendSMS(mobile, newCode.code, 'ali', function (err, body) {console.log(body);if (err)console.log("短信驗證碼發送失敗:", err);});callback({code: 0, message: "驗證碼已經發送"});} else {callback({code: -1, message: "驗證碼發送失敗,請重試"});}})}) 復制代碼項目開發過程中使用promise.all的代碼,當時是為了實現短信驗證碼發送前的校驗功能。 all中的兩個promise,第一個是統計時間內該手機號發送驗證碼數量;第二個是統計時間內該ip發送驗證碼的數量。
Promise使用過程中注意事項與誤區
注意事項在上面原理講解過程中,基本都提到過,只是重要的事情多說兩遍。
- 狀態不可逆性
- resolve函數和reject函數只是傳遞異步結果
- then進行層級調用的時候,每次的返回值都一個空promise對象,如果想繼續使用,賦值替換掉空promise對象,但是返回的時候return關鍵字很重要,不要忘了。
- promise.all中的執行順序是并行的,但是會等全部完成的結果傳遞給then
- ==執行順序==,promise是then方法調用之后才會執行嗎?還是從創建那一刻就開始執行? promise從創建那一刻就開始執行,只是把結果傳遞給了then,then與promise的執行無關。
Promise的反思
Promise的講解就到這里,但是大家在開發過程中,會發現有些時候多次操作異步會出現很多層級的調用,也就是
promise.then(...).then(...)
復制代碼.then(...) 復制代碼
這種情況,代碼雖然看起來會比callback的回調簡介和規范了很多,但是還是感覺一些復雜,有沒有更好的解決辦法呢?請看下一篇博客
回調的終極使用--async和await的講解
覺得本文對你有幫助?請分享給更多人 我的公眾號.jpg歡迎大家關注我的公眾號——程序員成長指北。請自行微信搜索——“程序員成長指北”
總結
以上是生活随笔為你收集整理的回调地狱解决方案之Promise的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么?ES6 中还有 Tail Call
- 下一篇: webpack 4.0 小记