0、前言
????????處在前后端分離開發(fā)模式的時代抄淑,前端向后端請求數(shù)據(jù)似乎已經(jīng)司空見慣觉至,在稍微復(fù)雜一點的業(yè)務(wù)中就可能遇到串行接口的情況,這就會產(chǎn)生回調(diào)嵌套助赞,回調(diào)過深會導(dǎo)致代碼可維護性差,因此需要一種解決回調(diào)過深的方案:promise赐劣。ES6 promise語法精深嫉拐,本文只介紹工作中最常用到的promise相關(guān)知識。
1魁兼、promise語法
- 創(chuàng)建promise
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
//實例
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
創(chuàng)建promise最重要的操作就是確定何時執(zhí)行resolve方法
- Promise.prototype.then方法
- 雖然then方法接受兩個函數(shù)作為參數(shù),但一般只寫成功回調(diào),并用catch代替失敗回調(diào)
//bad
promise.then(successFn,errorFn)
//good
promise.then(successFn).catch(errorFn)
- 根據(jù)successFn返回的結(jié)果咐汞,then有不同的返回值,then方法總是返回一個新的promise:
2.1 當successFn返回普通值v1時盖呼,then返回原來的promise,并將v1作為后續(xù)then方法的成功回調(diào)參數(shù)化撕;
2.2 當successFn返回已經(jīng)被resolved的promise時几晤,then返回原來的promise,并將successFn返回的promise值作為后續(xù)then方法的成功回調(diào)參數(shù)植阴;
2.3 當successFn返回一個未被resolved的promise1時蟹瘾,then方法返回的promise將與promise1具有一致的狀態(tài),可以看成then方法返回了promise1
- Promise.prototype.resolve方法
根據(jù)resolve方法接受的參數(shù)類型分為:
1掠手、promise憾朴,直接返回這個promise
2、普通類型(不包含then方法的對象)喷鸽,返回一個立即resolve的promise
3众雷、不傳參數(shù),返回一個立即resolve的promise - Promise.prototype.all方法
接受promise組成的數(shù)組做祝,并能使結(jié)果有序返回砾省。缺點是一旦其中一個promise被rejected,所有數(shù)據(jù)都拿不到了混槐。返回的結(jié)果以數(shù)組形式存儲编兄,可按下標依次取出
2、promise運用場景
1声登、單個請求promise化
//異步加載圖片
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
loadImageAsync(url).then(successFn).catch(handleError)
2翻诉、多個串行請求promise化
場景:先拿用戶信息,再獲取數(shù)據(jù)
//方案1
getUserInfo().then(getData).then(successFn).catch(handleError)
方案2
async function fn(){
let userInfo=await getUserInfo();
let data=await getData(userInfo);
return data;
}
fn().then(successFn).catch(errorFn);
方案2的async函數(shù)寫法使得串行的鏈式調(diào)用變成了同步寫法捌刮,語義更加清楚
3碰煌、多個并行請求promise化
場景:一次性拿不同tab下的數(shù)據(jù)
方案1:使用Promise.all()方法
let tab1_promise=getHotData();
let tab2_promise=getLatestData();
let arr=[tab1_promsie,tab2_promise];
Promise.all(arr).then(([tab1_data,tab2_data])=>handleFn).catch(errorFn);
這種方式顯然有風險,可考慮如下方案2
方案2:順序執(zhí)行promise
let arr=[tab1_promise,tab2_promise];
arr.reduce((task,promise)=>{
return task.then(()=>return promise).then(successFn);
},Promise.resolve());
這種方案可以對每個promise的返回值單獨處理绅作,不必等到所有數(shù)據(jù)一起返回才處理芦圾,更加靈活
3、總結(jié)
- 創(chuàng)建一個promise的關(guān)鍵在于確定resolve的執(zhí)行時機
- then方法總是返回一個新的promise
- 使用這種寫法promise.then(successFn).catch(errorFn),而不是promise.then(successFn,errorFn)
- Promise.reslove()可以創(chuàng)造一個立即resolve的promise俄认,結(jié)合reduce方法可使代碼更加精煉
- async函數(shù)基于Promise个少,可解決串行鏈式調(diào)用過長的問題,并且語義清楚眯杏,推薦使用
- Promise.all方法有風險夜焦,盡量不用
4、超時和可取消的Promise
// 可取消的Promise(本質(zhì)上并沒有取消請求岂贩,只是不用promise的返回值而已)
export default function makeCancelable(promise){
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then((val) =>
hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
);
promise.catch((error) =>
hasCanceled_ ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
}
// 超時的Promise
export const makeTimeoutPromise = ({ timeout = 300000, callback,promise }) => {
// 默認5分鐘超時
let hasTimeout = false;
let timer = setTimeout(() => {
hasTimeout = true;
}, timeout);
const wrappedPromise = new Promise((resolve, reject) => {
promise.then((val) =>
hasTimeout ? reject({hasTimeout: true}) : resolve(val)
).catch((error) =>
hasTimeout ? reject({hasTimeout: true}) : reject(error)
).finally(()=>timer && clearTimeout(timer))
}
return wrappedPromise;
};
掌握以上這些茫经,應(yīng)該能夠應(yīng)對工作中JS的異步處理了
https://mp.weixin.qq.com/s/cN40pHBfttZ3O2oEbVPAcg