Andy's blog

If you always do what you've always done, you'll always get what you've always got.

0%

2021-07-18-[筆記]Promise 完整介紹

前言:
目前前端主要都是透過 AJAX 方法來非同步取得遠端資料,像是 jQuery、Fetch 都是基於 AJAX 概念下,所開發的 API。而我們今天要介紹的 Promise 有下面幾點特色。

  1. 解決了過往不同寫法的差異(如:$.ajax)
  2. 解決了過於巢狀的寫法。使整體結構更優於管理。

參考資料:
Day23-非同步實作篇 II!Promise
JavaScript Promise 全介紹
Promise MDN


歷史

開始前,我想先介紹一下在還沒有 $.ajax 這個 API 出現前,網頁該如何遠端取得資料呢?
我們這時候就必須透過建立 XMLHttpRequest 物件,來進行資料取得。方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1. 建立物件
var http = new XMLHttpRequest();

//2.發出請求
http.open("get",'https://randomuser.me/api/',true);

//3.判斷回傳狀態
http.onreadystatechange = function(){
if(this.status === 200){
console.log("成功")
}
};

//4.送出
http.send();

寫完,我們可以發現,這樣的寫法在管理上其實非常不方便,也會讓程式碼不易閱讀。進而有了 $.ajax 這個 API 產生。

但是我們在使用過後,其實發現 jQuery 方法雖然解決上述問題,但對於過於巢狀根同步處理所有非同步行為上還是有一定難度。

進而有了 Promise 物件的產生,讓我們可以更方便實作 AJAX

簡介

Promise 本身是用來改善 JavaScript 非同步的語法結構。而Async、Await則是基於Promise語法讓非同步的語法的結構類似於 同步語言,更易讀且好管理。

結構

Promise 本身是一個建構函式,我們可以透過console.log看到下列四種方式

1
2
3
4
Promise.all() //傳入陣列
Promise.race() //傳入陣列
Promise.resolve()
Promise.reject()

圖片

Promise 建構函式 new 出的物件,則可以使用其中的原型方法(在 prototype 內),其中就包含 then、catch、finally,這些方法則必須在新產生的物件下才能呼叫。

1
2
3
4
let AJAJ = new Promise( () => { });
AJAJ.then(); //Promise回傳正確
AJAJ.catch(); //Promise回傳錯誤
AJAJ.finally(); //非同步執行完畢(無論是否正確完成)

此外,建立新的建構函式時,必須傳入一個函式作為參數(executor function),此函式的參數包含 resolve, reject,這兩個方法分別代表成功與失敗的回傳結果,特別注意這兩個僅能回傳其中之一,回傳後表示此 Promise 事件結束。

1
2
3
4
let AJAJ = new Promise( function(resolve,reject){
resolve(); //回傳成功
reject(); //回傳失敗
});

狀態

  • Pending -> 尚未得到結果
  • Resolved:事件已經執行完畢且成功操作,回傳 resolve 的結果
  • Rejected:事件已經執行完畢但操作失敗,回傳 rejected 的結果

圖片
圖片來源:卡斯伯文章

接著,不論成功或失敗我們可以透過thencatchfinally來取得結果。

補充一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在 .then(onFulfilled, onRejected)中可帶入兩個回呼函式 ,只是實務上我們很少這樣做就是
onFulfilled:執行成功的函式,所帶入參數表示 Promise 函式中 resolve 所帶入的值。
onRejected:執行失敗的函式,帶入參數表示 Promise 函式中 reject 所帶入的值。

//寫法一,在 then 鏈結中加入兩個 callback Function
promise().then((success) => {
console.log('success')
}, (fail) => {
console.log('fail')
})

//寫法二
promise()
.then(success => {
console.log('success')
})
.catch(fail => {
console.log('fail')
})
.finally(last =>{
console.log('最終結果')
})

實作

要將一個觀念熟記,自己動手寫一遍是最恰當的。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 1. 創建自己 promise
let Promise = new Promise();
// 這樣撰寫,會出現Uncaught TypeError: Promise resolve undefined is not a function
// 因為我們在建立 Promise 實體時必須接一個 callback Function

// 2. Promise 有三種狀態:pending、fulfilled、reject
// 3. 註記:回傳狀態只會有一種,resolve 或 reject

let newPromise = new Promise((resolve, reject) => {
resolve("success");
//reject("fail");
});

// 4. Promise 大部分都是透過 then 接受成功結果, reject 接受回傳失敗
// ,但其實我們也可以在 then 裡面接受失敗結果喔,只是通常我們不會這樣做就是
newPromise
.then((res) => {
console.log(1, res);
}).catch(error => {
console.log(error);
});

console.dir(newPromise);

function chainPromise(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (num) {
resolve(`${num} success`);
}
else {
reject(`${num} fail`);
}
}, 10);
});
}

// 5. 透過在 then 串接中,加入兩組 callback Function,使得函式可以接受到成功跟失敗狀態
chainPromise(0)
.then((res =>{
console.log(res);
}),(reject =>{
console.log(reject);
}));

chainPromise(0)
.then(res => {
console.log(res);
})
.catch(error => {
console.log(error);
})