JavaScrip 是單線程,前端在大部分情況下寫出來的 JS 代碼都是同步執(zhí)行。偶爾會通過回調(diào)函數(shù)達到異步執(zhí)行的目的。后來有了 promise 對象解決異步問題,被納入到 ES6 標準中。但是很多人都不理解 promise,或者一知半解,感覺懂又好像不懂,最主要的是在實際項目中用得少,不能夠?qū)W以致用,融會貫通。但也正是因為不能夠真正理解所以也不敢貿(mào)然用在項目中,于是兩者陷入循環(huán),導致很多人對這個既能夠提高代碼質(zhì)量,又在一定程度上提升性能的對象棄之不用。 我從前對 promise 的印象也是停留在以上階段,直到今天,在一個實際開發(fā)案例中運用到了 promise ,讓我對 promise 有了深刻的理解,在此分享給大家。 案例 在以上的頁面中,有一個點贊功能,是上級看過記錄之后進行評價的反饋。 上級打開下屬的記錄詳情之后,涉及到的邏輯如下:
調(diào)登陸接口,拿到上級userId。 調(diào)點贊列表接口。 通關(guān)上級的 userId,與點贊列表接口里面對應(yīng)的點贊 id 比較,如果上級點過贊,則點贊的圖標是實心。否則是空心。
案例分析實現(xiàn)以上功能需要調(diào)兩個接口。首先調(diào)登陸接口,拿到 userId,然后在回調(diào)中再調(diào)點贊列表接口,循環(huán)列表,拿到點贊 id ,與 userId 做比較。代碼如下: 第一步:調(diào)用登陸接口,拿到 userId function getUserId(cb){
axios.post(Config.serverUrl "/login/login",
{code: result.code,
url:url,}, {
headers: {'Content-Type': 'application/json'}
}).then(res => {
if (res.data.isSuc == true) {
sessionStorage.setItem(Config.SESSION_DING_USER,JSON.stringify(res.data.result));
let s = sessionStorage.getItem(Config.SESSION_DING_USER);
let user = JSON.parse(s);
let userId = user && user.userid;
if(cb){
cb(userId);
}
} else {
alert(登錄失敗);}
})
}
第二步,調(diào)用點贊列表接口 function getPraiseList(cb){
axios.post(Config.serverUrl "/visitAndSign/getpraiseInfo", {
"signId": Number(self.id),
}, {
headers: {'Content-Type': 'application/json', 'DING_TOKEN': self.token}
}).then(function (res) {
if (res.data.isSuc) {
let list = res.data.result;
if(cb){
cb(list);
}
}
})
}
第三步,根據(jù)回調(diào)結(jié)果,判斷是否點過贊 getUserId(function (userId){
getPraiseList(function(list){
for (var i=0;i<list.length;i ){
if(list[i].praiseUserid == userId){
self.isPraise = 0;
break;
}
}
})
})
通過 Promise 對案例進行優(yōu)化以上通過兩層回調(diào)可以實現(xiàn)業(yè)務(wù)邏輯,咋一看沒什么大問題,但是如果業(yè)務(wù)還沒完,假設(shè)又增加一個需求,根據(jù)點贊狀態(tài)判斷是否調(diào)用另一個接口。這樣就會陷入回調(diào)地獄,回調(diào)地獄主要是指回調(diào)太多,代碼冗長,到最后很有可能你都不知道那個回調(diào)對應(yīng)的那個參數(shù)了。 為了優(yōu)雅的解決以上問題,Promise 對象閃亮登場,現(xiàn)在使用 Promise 實現(xiàn)以上功能。并且假設(shè)得到是否點贊狀態(tài)之后還要調(diào)第三個接口。 第一步優(yōu)化,使用 Promise 對象和 .then() 方法 var promise = new Promise(function(resolve,reject){
axios.post(Config.serverUrl "/login/login",
{code: result.code,
url:url,}, {
headers: {'Content-Type': 'application/json'}
}).then(res => {
if (res.data.isSuc == true) {
sessionStorage.setItem(Config.SESSION_DING_USER,JSON.stringify(res.data.result));
let s = sessionStorage.getItem(Config.SESSION_DING_USER);
let user = JSON.parse(s);
let userId = user && user.userid;
resolve(userId);
}})
})
promise.then(function(userId){
axios.post(Config.serverUrl "/visitAndSign/getpraiseInfo", {
"signId": Number(self.id),
}, {
headers: {'Content-Type': 'application/json', 'DING_TOKEN': self.token}
}).then(function (res) {
if (res.data.isSuc) {
let list = res.data.result;
for (var i=0;i<list.length;i ){
if(list[i].praiseUserid == userId){
self.isPraise = 0;
break;
}
}
return self.isPraise;
}
})
})
.then(function(res){
console.log(res)//點贊狀態(tài)實現(xiàn)結(jié)果,這里可以繼續(xù)調(diào)第三個接口
})
//后面如果還有回調(diào)繼續(xù)用.then()即可。
第二步優(yōu)化,使用 Promise.all 優(yōu)化性能 雖然通過 Promise 對象解決了代碼展示問題。還有一個問題,等到登陸接口調(diào)成功之后再調(diào)點贊列表接口。但是實際上點贊列表接口并不依賴于登陸接口。所以登陸接口和點贊列表接口可以同時調(diào),加快接口返回速度,但問題是,做最后的對比處理(點贊列表編號與用戶編號對比來判斷用戶是否點過贊)需要在兩個接口都成功返回才可。所以這時候就用上了 Promise.all。 Promise.all 接收一個數(shù)組,而數(shù)組中就是每一個 Promise 對象。假設(shè) Promise.all 的返回結(jié)果定義為 pro。pro.then()就是兩個接口都調(diào)成功之后的回調(diào)。 對以上代碼進一步改造如下: var promise1 = new Promise(function(resolve,rejuect){
axios.post(Config.serverUrl "/login/login",
{code: result.code,
url:url,}, {
headers: {'Content-Type': 'application/json'}
}).then(res => {
if (res.data.isSuc == true) {
sessionStorage.setItem(Config.SESSION_DING_USER,JSON.stringify(res.data.result));
let s = sessionStorage.getItem(Config.SESSION_DING_USER);
let user = JSON.parse(s);
let userId = user && user.userid;
resolve(userId);
}})
})
var promise2 = new Promise(function(resolve,rejuect){
axios.post(Config.serverUrl "/visitAndSign/getpraiseInfo", {
"signId": Number(self.id),
}, {
headers: {'Content-Type': 'application/json', 'DING_TOKEN': self.token}
}).then(function (res) {
if (res.data.isSuc) {
resolve(res.data.result);
}
})
})
})
var pro = Promise.all([promise1,promise2]);
pro.then(function(userId,list){
for (var i=0;i<list.length;i ){
if(list[i].praiseUserid == userId){
self.isPraise = 0;
break;
}
}
})
總結(jié)Promise 對象用法
var promise = new Promise(function(resolve,rejuct){
if(){
//異步方法調(diào)用成功
resolve();
}else{
//異步方法調(diào)用失敗
rejuct();
}
})
promise.then(function(){
})
舉例: var promise = new Promise(function(resolve,reject){
setTimeout(function (res) {
resolve(res);
},2000,1);
})
promise.then(function(id){
console.log(id)//1
})
Promise.all() 用法
var promise1 = new Promise(function(resolve,rejuct){
if(){
//異步方法調(diào)用成功
resolve();
}else{
//異步方法調(diào)用失敗
rejuct();
}
})
var promise2 = new Promise(function(resolve,rejuct){
if(){
//異步方法調(diào)用成功
resolve();
}else{
//異步方法調(diào)用失敗
rejuct();
}
})
var pro = Promise.all([promise1,promise2])
pro.then(function([a,b]){
//在這里a和b都可以拿到
})
舉例: var promise1 = new Promise(function(resolve,reject){
setTimeout(function (res) {
resolve(res);
},2000,1);
})
var promise2 = new Promise(function(resolve,reject){
setTimeout(function (res) {
resolve(res);
},3000,2);
})
var pro = Promise.all([promise1,promise2]);
pro.then(function([a,b]){
console.log(a b)//3
})
雖然不使用一些新語法和高階用法也能實現(xiàn)一些功能,但是新語法和用法的誕生必定是解決了某個問題或提升了性能。所以在平時項目中還是要經(jīng)常將一些高階用法運用進去,如果不去實踐,有些概念很難真正理解。
|