Promise的使用——解決回撥地獄
阿新 • • 發佈:2018-11-22
回撥地獄
在沒有學會promise之前,當我遇到連續使用多個ajax請求的情況,並且做ajax請求時,這個新的ajax請求的其中一個引數,得從上一個ajax請求中獲取時,就需要回調函式套回撥函數了,就很可能出現回撥地獄的情況
比如以下程式碼,註釋是精華
//假設我已經引用了jQuery
$("button").click(function(){
$.get("demo_test.asp",function(result){
console.log("假裝這裡有一些程式碼");
//這裡就需要拿到這次ajax請求返回的資料,拼接成新的url,用於再次發起第二個ajax請求,所以第二個請求要寫在第一個請求的callback中
var url = "demo_test.asp?name:"+result;
$.get(url,function(result2){
console.log("假裝這裡又有一些程式碼");
/*
如果我還要發第三個請求,而且第三個請求需要用到第二個請求返回的資料,
那麼又需要將第三個請求寫在第二個請求的callback中的,我們再極端的想想,
如果這樣的情況要重複10次,20次,那樣就會有10個,20個回撥函式套回撥
函式這樣就會出現回撥地獄了。然鵝,Promise很好的解決了這個問題。
*/
});
} );
});
看看Promise如何解決回撥函式
//假設我已經引用了jQuery
function test(){
$.get("demo_test.asp",function(result){
console.log("假裝這裡有一些程式碼");
// 返回Promise物件
return new Promise(function(resolve) { //這裡還可以傳入第二個引數reject(可選)
resolve(result);
});
});
};
$("button").click(()=>{
test()
. then((result)=>{
var url = "demo_test.asp?name:"+result;
return url;//返回一個Promise物件,帶有引數url,可以直接在下一個then中使用
})
.then((url)=>{
$.get(url,function(result2){
console.log("假裝這裡又有一些程式碼");
return result2; //這裡又可以把請求的結果返回出來,接著用then做下一個請求
});
})
.then((result2)=>{
//巴拉巴拉又是一堆程式碼
})
});
這樣就完美地解決了回撥地獄的問題啦
下面再看一組程式碼,更加熟悉一下Promise的用法
以下程式碼來自 作者:這波能反殺,來源:簡書
function want() {
console.log('這是你想要執行的程式碼');
}
function fn(want) {
console.log('這裡表示執行了一大堆各種程式碼');
// 返回Promise物件
return new Promise(function(resolve, reject) {
if (typeof want == 'function') {
resolve(want); //等函式執行後,執行resolve(),resolve()就是我們在下面 .then()裡面寫的第一個函式,第二個函式可以不寫(第二個函式是reject())
} else {
reject('TypeError: '+ want +'不是一個函式')
}
})
}
fn(want).then(function(want) {
want();
})
fn('1234').catch(function(err) {
console.log(err);
})
再來看看Promise.all
Promise.all可以將多個Promise例項包裝成一個新的Promise例項。同時,成功和失敗的返回值是不同的,成功的時候返回的是一個結果陣列,而失敗的時候則返回最先被reject失敗狀態的值。
Promse.all在處理多個非同步處理時非常有用,比如說一個頁面上需要等兩個或多個ajax的資料回來以後才正常顯示,在此之前只顯示loading圖示。
需要特別注意的是,Promise.all獲得的成功結果的數組裡面的資料順序和Promise.all接收到的陣列順序是一致的,即p1的結果在前,即便p1的結果獲取的比p2要晚。這帶來了一個絕大的好處:在前端開發請求資料的過程中,偶爾會遇到傳送多個請求並根據請求順序獲取和使用資料的場景,使用Promise.all毫無疑問可以解決這個問題。
上面這段話引用至https://www.jianshu.com/p/7e60fc1be1b2,作者:李悅之,來源:簡書
我們接下來來看看下面這段程式碼
來自:破解前端面試(80% 應聘者不及格系列):從 閉包說起
const tasks = [];
for (var i = 0; i < 5; i++) {
((j) => {
tasks.push(new Promise((resolve) => {
setTimeout(() => {
console.log(new Date, j);
resolve(); // 這裡一定要 resolve,否則程式碼不會按預期 work
}, 1000 * j); // 定時器的超時時間逐步增加
}));
})(i);
}
Promise.all(tasks).then(() => { //這個resolve沒有傳引數,如果傳入引數,就會去獲取每一個Promise的resolve傳遞的值,收集為一個數組,作為這個resolve()的引數,如果不太明白,可以看下一個例子
setTimeout(() => {
console.log(new Date, i);
}, 1000); // 注意這裡只需要把超時設定為 1 秒
});
/*
每隔一秒輸出一行
Tue Oct 09 2018 16:26:19 GMT+0800 (中國標準時間) 0
Tue Oct 09 2018 16:26:20 GMT+0800 (中國標準時間) 1
Tue Oct 09 2018 16:26:21 GMT+0800 (中國標準時間) 2
Tue Oct 09 2018 16:26:22 GMT+0800 (中國標準時間) 3
Tue Oct 09 2018 16:26:23 GMT+0800 (中國標準時間) 4
Tue Oct 09 2018 16:26:24 GMT+0800 (中國標準時間) 5
*/
我們對上面的那段程式碼進行小小的改造
const tasks = [];
for (var i = 0; i < 5; i++) {
((j) => {
tasks.push(new Promise((resolve) => {
setTimeout(() => {
console.log(new Date, j);
resolve(j); // 傳參j,如果裡面沒有傳參,下面的Promise.all就會等到一個length為5的陣列,值都是undefined
}, 1000 * j); // 定時器的超時時間逐步增加
}));
})(i);
}
Promise.all(tasks).then((i) => { //這裡的i表示一個數組[0,1,2,3,4]
setTimeout(() => {
console.log(new Date, i); //猜猜這裡會輸出什麼?
}, 1000);
});
/*
每隔一秒輸出一行
Tue Oct 09 2018 16:26:19 GMT+0800 (中國標準時間) 0
Tue Oct 09 2018 16:26:20 GMT+0800 (中國標準時間) 1
Tue Oct 09 2018 16:26:21 GMT+0800 (中國標準時間) 2
Tue Oct 09 2018 16:26:22 GMT+0800 (中國標準時間) 3
Tue Oct 09 2018 16:26:23 GMT+0800 (中國標準時間) 4
Tue Oct 09 2018 16:32:30 GMT+0800 (中國標準時間) (5) [0, 1, 2, 3, 4]
*/
現在是不是對Promise有個簡單的瞭解啦