Promise是什么?
? ? Promise是異步編程的一種解決方案,比傳統的解決方案--回調函數和事件--更加合理和強大。最早是在社區提出和實現,ES6將其寫進了語言標準,統一了語法,原生提供了Promise對象。
? ? 好吧,那Promise到底是什么?嗯。。。。貌似可以提升逼格¥0#¥@
? ? 還是少一點套路直接上例子比較好理解。在敞開心扉之前,先請客官大人記住Promise的2大特性,①Promise的對象狀態不受外界影響。②一旦狀態改變,就不會再變,任何時候都可以得到這個結果。(試想你的代碼中有這么個對你忠貞的姑娘,484灰常激動。)當然,Promise也有一些小缺憾,后文會提到。
? ? 場景1:
? ? ? ? $.ajax({
? ? ? ? ? ? ****
? ? ? ? ? ? success:dosomething(data){}
? ? ? ? })
這是個最簡單的AJAX異步獲取數據的場景,一切都顯得那么和諧完美無瑕,簡直666,
好了,老板說我們的業務變得復雜了?
? ? 場景2:
? ? ? ?$.ajax({
? ? ? ? ? ?****
? ? ? ? ? ?success:fn(data){
? ? ? ? ? ? ? ?$ajax({
? ? ? ? ? ? ? ?***
? ? ? ? ? ? ? ?success:dosomethis(data){
? ? ? ? ? ? ? ? ? ?...
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?})
? ? ? ? ? ?}
? ? ? ?)}
這個場景描述描述的是一個AJAX請求依賴于另一個AJAX請求成功后獲得的返回值,我們寫出了兩層嵌套的AJAX連環炮,你似乎已經能看到你的未來了,隨著我們的業務變的越來越復雜,你的代碼可讀性會變的越來越差,你的連環炮越打越長,到后面你自己也找不到中間到底哪里會出錯,會出什么錯。這顯然不夠優雅~
場景2會輕松形成一個叫回調地獄的場景,可以讓你的代碼維護者花上一整天來弄懂你的這一些列連環炮到底是做什么的。這似乎是個報仇的好辦法。好了,這并不好,作為一名優雅的不行的前端開發工程師,是時候來一波無形裝逼了。
Promise基本用法
? ? var promise = new Promise(function(resolve,reject){
? ? ? ? // ... some code if(/* 異步操作成功 */){
? ? ? ? ? ? resolve(value);
? ? ? ? }else{
? ? ? ? ? ? reject(error);}
? ? ? ? });
Prominse構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve和reject。他們是兩個函數,由JavaScript引擎提供,不用自己部署。(通俗點講就是。一個帶了兩個奇怪的函數作為參數的函數把你的異步代碼包裹起來了作為參數給了Promise構造器造了一個Promise實例。優雅么,酸爽么,%……%¥)
那函數里面是什么鬼,回顧剛剛讓你特別記住的兩點的。就是每個Promise對象都有一個保存保存狀態的功能,當Promise實例被創建出來的時候,她有了第一個狀態Pending,然后被你包裹的那段異步代碼開始執行,&……¥¥#*,如果一切ok,異步操作成功了,執行埋伏已久的resolve函數,并且把需要處理的返回值作為參數傳遞出去,Promise對象的狀態從Pending置成Resolved(又稱Fulfilled),要是其中發生了什么亂七八糟牛鬼蛇神的事情導致的異步操作失敗了,執行reject函數并將錯誤信息傳遞出去,Promise對象的狀態就會從Pending置成Rejected,你還需要記住剛剛的第二點,這個狀態的改變不可逆且不可重復改變。這就是Promise承若的名字由來吧,多么美麗美好,仿佛看見十八歲的妹子在對你Promise Promise P...。
Promise實例生成以后,可以用then的方法指定Resolved狀態和Reject狀態的回調函數。先看寫法
? ? promise.then(function(value){
? ? ? ? // success
? ? },function(error){
? ? ? ?// failure
? ? });
then方法可以接收兩個回調函數作為參數,第一個回調函數是Promise對象的狀態變成Resolved時的回調,第二個則是Promise對象的狀態變為Rejected時調用。第二個參數是可選的,這兩個函數都自動接收Promise對象傳出的值作為參數。
說了這么多,我們來看看如何改下我們之前的場景2
首先我們把異步操作都交給Promise去實例一個Promise對象,并且封裝到一個方法作為返回值以供鏈式調用。
function doFir (){
? ? return new Promise((resolve,reject)=>{
? ? ? ? $.ajax({
? ? ? ? ? ? ****
? ? ? ? ? ? success:resolve(value)? //成功后改變Promise的狀態并且將value交給resolve傳遞出去
? ? ? ? ? ? error:reject(err)? //失敗后 將Promise狀態Rejected并且將err交給reject傳遞出去
? ? ? ? })
? ? }) ?//整個Promise實例作為返回值返回出去
}
function doSec(value){
? ? return new Promise((resolve,reject)=>{
? ? ? ? $.ajax({
? ? ? ? ? ? ****
? ? ? ? ? ? success:resolve(value)? //成功后改變Promise的狀態并且將value交給resolve傳遞出去
? ? ? ? ? ? error:reject(err)? //失敗后 將Promise狀態Rejected并且將err交給reject傳遞出去
? ? ? ? })
? ? })? //整個Promise實例作為返回值返回出去
}
function doThi(value){
? ? return new Promise((resolve,reject)=>{
? ? ? ? $.ajax({
? ? ? ? ? ? ****
? ? ? ? ? ? success:resolve(value)? //成功后改變Promise的狀態并且將value交給resolve傳遞出去
? ? ? ? ? ? error:reject(err)? //失敗后 將Promise狀態Rejected并且將err交給reject傳遞出去
? ? ? ? })
? ? })? //整個Promise實例作為返回值返回出去
}
為了體現Promise的優雅,多加了一個異步操作,注意現在三個任務函數的關系是并列的,他們共享同一個作用域,在作用域鏈中的位置也是同級的,作用域鏈不理解的同學跳過沒關系。然后開始展現Promise的優雅。bi~~~u~~
doFir().then((value) => {doSec(value)})
? ? ? ? ? ?.then((value) => {doThi(value)})
QAQ!~~~好吧我們稍微拆解一下說明其中的那些優雅
doFir()
? ? ? ? ? .then((value) => {doSec(value)})//這里then省略了第二個參數,也就是Promise為Rejected時候的回調,第一個? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //回調函數會接收到success:resolve(value)傳遞出來的value參數,開始執行? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //doSec()
? ? ? ? ? .then((value) => {doThi(value)})//因為doSec()返回的是一個新的Promise對象,一樣的你可以接著then
至此,一個嵌套的多層級的異步代碼塊通過使用Promise扁平化了,閱讀者和編程者都能快速清晰的知道這一串鏈式調用到底要做什么。
好吧 優雅的和大家說88~