前記:本篇文章不是基礎(chǔ)知識(shí)科普篇,而是實(shí)際工作中的記錄篇,以實(shí)際工作需求帶出前端請(qǐng)求處理的演變歷程。
開篇先來一個(gè)回調(diào)callback的例子(來自微信小程序 Api):
wx.request({
url: 'test.php', //僅為示例,并非真實(shí)的接口地址
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json' // 默認(rèn)值
},
success (res) {
console.log(res.data)
}
})
赤裸裸的success回調(diào),設(shè)想一下如果我request里面嵌request甚至再嵌好幾個(gè)~~~那畫面就叫回調(diào)煉獄。
改造成promise形態(tài),學(xué)習(xí)最簡(jiǎn)單的promise方法封裝
再不改造更待何時(shí),第一個(gè)浮現(xiàn)在腦海的東西自然是promise,我們先來改造request,當(dāng)然我改造了不少小程序原有的api。我給這些原有api都采用了promise化的賦能。在wx方法里面獨(dú)立出來了wx.top庫。于是我的request變成了wx.top.request,使用參數(shù)不變。
wx.top = {};
wx.top.request = function(){
return new Promise((resolve, reject){
wx.request({
url: args.url, //僅為示例,并非真實(shí)的接口地址
data: args.data,
//header:這里你按需定制比如全局token
success (res) {
//這里也可以根據(jù)后端通訊規(guī)范,設(shè)置自己的relove和reject
relove(res);
},
fail (res){
reject(res);
}
});
});
}
此時(shí)的request數(shù)據(jù)返回怎么處理
wx.top.request({
url : "xxx",
data : {}
}).then(res=>{
console.log(res); //請(qǐng)求的返回信息
});
以上是promise最直接的用法,封裝方式就是return new Promise對(duì)象即可。Promise化的方法,你只需要關(guān)注兩點(diǎn),什么時(shí)候resolve什么時(shí)候reject。
在我們項(xiàng)目上線后的一個(gè)月,突然有一天我接到一個(gè)工單,說是直接下單不能買了,跟后端反復(fù)查實(shí),最后找到了問題,原來是下單數(shù)據(jù)依賴的接口請(qǐng)求有好幾處(拉取地址信息,計(jì)算運(yùn)費(fèi),計(jì)算個(gè)人賬戶余額),很容易出現(xiàn)還沒算好運(yùn)費(fèi)就被用戶點(diǎn)了購(gòu)買的情況,我試圖去用三個(gè)標(biāo)記去判斷可以提交,攔截是可以,代碼可讀性不好,然后又沒辦法做友好度提示。于是我又做了如下改進(jìn):
利用promise封裝原子方法,利用promise實(shí)現(xiàn)接口以來,利用promise實(shí)現(xiàn)統(tǒng)一狀態(tài)管理
一、計(jì)算運(yùn)費(fèi)方法:
calcPostFee(){ //獲取運(yùn)費(fèi)
return new Promise((resolve, reject) => {
if(isPost || !address){ //如果包郵或者沒有地址信息返回0
resolve(0);
}else{
orderFreight(address, order).then(res => { //order
//這里resolve(運(yùn)費(fèi))或者reject(錯(cuò)誤信息)
}).catch((err) => {
console.log(err); //接收錯(cuò)誤信息-信息包含請(qǐng)求錯(cuò)誤|代碼錯(cuò)誤
wx.top.showTip(getObjVal(err, ['data', 'msg'],true) || '獲取運(yùn)費(fèi)失敗'); //getObjVal是我自己封裝的js對(duì)象數(shù)據(jù)方法
reject(err);
});
}
});
},
二、獲取地址信息:
queryAddress(){ //獲取運(yùn)費(fèi)
return new Promise((resolve, reject) => {
queryUserAllAddress({ header : {token : this.userInfo.token} }).then(res=>{
//這里resolve(地址信息)或者reject(錯(cuò)誤信息);
}).catch((err)=>{
console.log(err);
wx.top.showTip(getObjVal(err, ['data', 'msg'],true) || '獲取地址失敗');
reject(err);
});
});
},
三、計(jì)算個(gè)人余額及訂單費(fèi)用方法:
calcOrder(order){ //計(jì)算余額及訂單費(fèi)用
return new Promise((resolve, reject) => {
//單純的計(jì)算沒有任何請(qǐng)求,這里我也想提醒大家一下promise是個(gè)工具,不是只有請(qǐng)求才可以用,萬物皆可promise
//這里resolve(正確信息)或者reject(錯(cuò)誤信息);
});
},
四、接口總統(tǒng)領(lǐng):
prepareData(){
return new Promise((resolve, reject)=>{
wx.showLoading({title : "訂單正在生成...", mask : true}); //開啟mask,不可點(diǎn)擊??梢越y(tǒng)一提示了,代碼再也不散亂了
this.queryAddress().then(()=>{ //地址信息回來之后
return this.calcPostFee(); //再計(jì)算運(yùn)費(fèi)
}).then(()=>{
return this.calcOrder(); //算好訂單數(shù)據(jù)之后
}).then(()=>{
reeolve();
wx.hideLoading();//解開遮罩,避免誤點(diǎn)
}).catch((err)=>{
wx.hideLoading(); //隱藏loading
});
});
}
prepareData.then(res=>{
this.setData({ disableBtn : false; }); //可以去購(gòu)買了
}).catch(()=>{
this.setData({ disableBtn : true; });//設(shè)置購(gòu)買按鈕不可點(diǎn)擊
})
下面我開始推出async await
這是https://javascript.info/async-await中的一句話,概括的很到位,
說async await是讓promise以更舒適更時(shí)髦的方式工作的語法,而且其極度容易理解和使用

先上個(gè)例子來讓大家走進(jìn)基本語法:
async prepareData(){
wx.showLoading({title : "訂單正在生成...", mask : true});
try{
let address = await this.queryAddress();
let fee = await this.calcPostFee();
let amt = await this.calcOrder();
wx.hideLoading();
return true;
}catch(e){//任意一個(gè)方法錯(cuò)誤(reject或者js錯(cuò)誤)都可以被捕獲
wx.hideLoading();
}
}
prepareData().then(...)
嗯,不錯(cuò)確實(shí)優(yōu)雅了很多,也洋氣了,await用的很傳神。就是捕獲錯(cuò)誤的方式有點(diǎn)不優(yōu)雅,好的,上另外一種方式:
async prepareData(){
let address = await this.queryAddress();
let fee = await this.calcPostFee();
let amt = await this.calcOrder();
}
//使用
wx.showLoading({title : "訂單正在生成...", mask : true});
prepareData().then(...).catch(...);
咿?這里的prepareData也可以then跟catch?
沒錯(cuò)兒,async 修飾詞就是讓方法體變成了Promise對(duì)象,等同于在方法體內(nèi)部return new Promise。
而await只能在async修飾的方法體內(nèi)部使用,作用也很明確,我去買橘子,你等著。
很多人說async await方法是回調(diào)的終極解決方案,我不認(rèn)同,我認(rèn)為其只是一種方法載體,讓Promise的寫法更優(yōu)雅。 請(qǐng)看如下實(shí)驗(yàn),fa方法執(zhí)行時(shí)間很明顯比fb執(zhí)行時(shí)間要長(zhǎng),但是await告訴fb你先等著,請(qǐng)看下例:
驗(yàn)證一下await的執(zhí)行順序
function fa(){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('a')
},2000);
});
}
function fb(a){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log(a);
resolve('b'+a)
},1000);
});
}
async function c(){
let a = await fa();
let b = await fb(a);
}
c().then(res=>{
console.log(res);
});
反思Promise有all跟race方法,怎么用async跟await去實(shí)現(xiàn)這樣的機(jī)制呢?也許有方式,但是我想說的還是它只是Promise的載體,跟最終的解決方案范疇有點(diǎn)出入。不管js以什么樣的方式什么樣的速度發(fā)展,我希望能夠出來一些解決真正業(yè)務(wù)痛點(diǎn)的語法,謝謝各位觀看者。