1. 程式人生 > >Promise的使用——解決回撥地獄

Promise的使用——解決回撥地獄

回撥地獄

在沒有學會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有個簡單的瞭解啦