JavaScript-Promise

JavaScript是單執行緒的(single threaded)程式語言,也就是一次只能做一件事情,然而在前端會需要處理許多耗時的非同步事件,例如:ajax call API、定時器(setTimeout)、事件監聽(eventListener),一般我們很常會使用callback回調的方式,然而一層包一層的callback就會出現回呼地獄(callback hell),層次太多的巢狀callback,讓程式變得複雜、難以追蹤。

為了解決這個問題,Promise由此而生。

Event Queue

首先,要理解JavaScript是如何運作的之前,要先認識事件佇列(Event Queue),當JavaScript在執行時,會先將「setTimeout」、「ajax」、「click」等非同步事件放入Event Queue,並且先執行其他Execution context的事件後,才會回過頭去執行Event Queue中的事件。

以下方程式碼舉例,第8行的setTimeout為非同步事件,會先被放入Event Queue中,等待其他事件都處理完畢後,才會回過頭來執行Event Queue中的事件,因此第8行的setTimeout的執行順序在最後面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function doWork(){
var mom = "媽媽";

//立即函式
(function(){
console.log('打給媽媽');//執行順序1

setTimeout(function(){
console.log(mom+'回應')//執行順序3
},3000)
})();

//立即函式
(function(){
console.log('洗碗')//執行順序2
})();
}

doWork(); //執行

//執行結果:
//打給媽媽
//洗碗
//媽媽回電

這裡在第10行 setTimeout 的時間修改成 0 ,即使等待時間為 0 秒,不過因為 setTimeout 本身為非同步事件會被放入Event Queue,仍然會等所有執行堆中的事件都處理完之後,才執行Event Queue的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function doWork(){
var mom = "媽媽";

//立即函式
(function(){
console.log('打給媽媽');//執行順序1

setTimeout(function(){
console.log(mom+'回應')//執行順序3
},0)
})();

//立即函式
(function(){
console.log('洗碗')//執行順序2
})();
}

doWork(); //執行

//執行結果:
//打給媽媽
//洗碗
//媽媽回電

設定一個Promise

Promise 是一個用來表示非同步事件執行結果為成功或失敗的物件,預設的情況有兩個 callback 參數,resolve 代表成功時會執行的函式, reject 代表失敗時會執行的函式。這兩個callback皆會在該非同步事件執行完畢之後呼叫,成功用 .then() 承接,失敗則用 .catch() 承接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 先定義一個非同步事件
const newFunc=(value)=>{
//設定一個Promise物件
return new Promise((resolve,reject)=>{
if(value){
resolve('成功');//成功則執行resolve callback方法
}
else{
reject('失敗');//失敗則執行reject callback方法
}
});
};

//呼叫執行非同步事件,結束後用.then()及.catch()承接
newFunc(value).then((response)=>{
console.log('執行結果:'+response);//執行結果:成功
}).catch((error)=>{
console.log('執行結果:'+error);//執行結果:失敗
});

實際執行結果

鏈結Promise

Promise 的出現是為了解決回呼地獄 (Callback Hell),所以他強大的地放是用於串連,也就是依序呼叫兩個以上的非同步事件,稱作 Promise Chain。

只要在 Promise chain 中的任一個事件出現錯誤,而執行呼叫 reject callback, Promise chain 的執行會直接中斷,進到catch中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//先定義一個非同步方法,其回傳值為一個 Promise 物件
const newFunc = (time) => new Promise((resolve,reject)=>{
//setTimeout 事件會被放進佇列中,其執行順序會在if之後
setTimeout(()=>{
resolve(`${time/1000}秒 成功`);
},time);

if(time === 0){
reject(new Error(`${time/1000}秒 失敗`));
}
});

//執行 Promise chain
//首個 newFunc resolve 的回傳值由 response1 承接
newFunc(1000).then((response1)=>{/
console.log('執行一:'+response1);
return newFunc(1000);//第二個 newFunc resolve 的回傳值由 response2 承接
}).then((response2)=>{
console.log('執行二:'+response2);
return newFunc(0);//第三個 newFunc resolve 的回傳值由 response3 承接
}).then((response3)=>{
console.log('執行三:'+response3);
return newFunc(1000);//第四個 newFunc resolve 的回傳值由 response4 承接
}).then((response4)=>{
console.log('執行四:'+response4);
}).catch((error)=>{//catch 承接每次 newFunc reject 拋出的 error
console.log('失敗:'+error)
})

實際執行結果

鏈結接續在catch後方

在 catch 之後,仍然可以接續 Promise chain。

1
2
3
4
5
6
7
8
9
10
11
12
13
newFunc(1000).then((response1)=>{/
console.log('執行一:'+response1);
return newFunc(1000);//第二個 newFunc resolve 的回傳值由 response2 承接
}).then((response2)=>{
console.log('執行二:'+response2);
return newFunc(0);//第三個 newFunc resolve 的回傳值由 response3 承接
}).then((response3)=>{
console.log('執行三:'+response3);
}).catch((error)=>{//catch 承接每次 newFunc reject 拋出的 error
console.log('失敗:'+error)
}).then(()=>{
console.log('最終執行');
})

執行結果

© 2020 Leah's Blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero