JavaScript - Promise

Promise

Promise 代表一個未來將要完成、或失敗的非同步操作。

比喻: 去公家機關尋求幫助前先拿號碼牌,拿到的號碼牌就像是 promise,等到紙張上的號碼到了(resolve),在櫃檯獲得的幫助就像是調用了 callback

建立 Promise

1
2
3
4
5
6
7
const myPromise = new Promise((resolve, reject) => {
resolve([1, 2, 3, 4]);
});

myPromise.then(arr => {
console.log("Promise myPromse resolved with data: ", arr);
});

  • 首先我們使用 promise 構造函數,構造函數內有單個回調函數,該回調函數具有兩個參數,resolve 和 reject

  • 因此,我們的非同步任務將根據我們自定義的回調函數來決定調用解決(resolve)或拒絕操作(reject),上面的例子直接 resolve

  • 現在我們創建好了 promise 物件,下一步要做的是在 then 函數中定義解決(resolve)後要做什麼事,這樣的就算是完成了一個基本 promise

處理 Error

1
2
3
4
5
6
7
8
9
const myPromise = new Promise((resolve, reject) => {
reject("ERROR");
});

myPromise.then(data => {
console.log("Promise myPromse resolved with data: "+ data);
}).catch(data => {
console.log("Promise myPromse rejected with data: "+ data);
});

  • 上一個範例只有處理成功的 promise,then 只處理 resolve 的情況,但是 promise 同樣也可以處理 reject,使用的是 catch 函數

  • catch 函數中定義拒絕(reject)後要做什麼事

用隨機產生 Error 來更熟悉 Promise 運作

1
2
3
4
5
6
7
8
9
10
const myPromise = new Promise((resolve, reject) => {
let randomNum = Math.random();
randomNum < 0.5 ? resolve(randomNum) : reject(randomNum)
});

myPromise.then(result => {
console.log("Success: "+ result);
}).catch(error => {
console.log("Error: "+ error);
});

加上 setTimeout 模擬非同步任務

1
2
3
4
5
6
7
8
9
10
11
12
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
let randomNum = Math.random();
randomNum < 0.5 ? resolve(randomNum) : reject(randomNum)
}, 2000);
});

myPromise.then(result => {
console.log("Success: "+ result);
}).catch(error => {
console.log("Error: "+ error);
});

Promise Chaining

首先看看沒有用 Promise 的巢狀非同步任務

1
2
3
4
5
6
7
8
9
10
11
12
13
let counter = 0;
setTimeout(() => {
counter++;
console.log("Counter: "+ counter);
setTimeout(() => {
counter++;
console.log("Counter: "+ counter);
setTimeout(() => {
counter++;
console.log("Counter: "+ counter);
}, 3000);
}, 2000);
}, 1000);

  • 很容易可以發現這樣一層包著一層的程式碼結構,很雜亂不好維護

  • 上面的例子還很單純,但是真實的情況會複雜很多,那就真的很難維護了

  • 不是模塊化,很難實現覆用

再改進上面的例子為 Promise 前,先來看看 Promise Chaining

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
let randomNum = Math.random();
resolve(randomNum);
}, 2000);
});

myPromise.then(num => {
console.log("num: "+ num);

return new Promise((resolve, reject) => {
setTimeout(() => {
num++;
resolve(num);
}, 2000);
});
}).then(result => {
console.log("result: "+ result);
});

  • 其實也只是在 then 回傳新的 Promise,這樣就能做到連續非同步的操作

而 Promise 不止能回傳 new Promise 還能回傳 value

1
2
3
4
5
6
7
8
9
10
11
const myPromise = new Promise((resolve, reject) => {
resolve(1);
});

myPromise.then(data => {
return data*2;
}).then(data => {
return data*2;
}).then(data => {
console.log(data)
});

回到巢狀非同步任務的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
let counter = 0;
setTimeout(() => {
counter++;
console.log("Counter: " + counter);
setTimeout(() => {
counter++;
console.log("Counter: " + counter);
setTimeout(() => {
counter++;
console.log("Counter: " + counter);
}, 3000);
}, 2000);
}, 1000);

換成使用 Promise

  1. 先把要做的事情拉出來

1
2
3
4
5
let counter = 0;
function incCounter() {
counter++;
console.log("Counter: " + counter);
}

  1. 主程式,在運行這個程式時會把 Promise return 出來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let counter = 0;

function incCounter() {
counter++;
console.log("Counter: " + counter);
}

function runLater(callback, timeInMs) {
const p = new Promise((resolve, reject) {
setTimeout(() => {
const res = callback();
resolve(res);
}, timeInMs);
});
return p;
}

  1. Promise Chaining 的方式串起來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let counter = 0;

function incCounter() {
counter++;
console.log("Counter: " + counter);
};

function runLater(callback, timeInMs) {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const res = callback();
resolve(res);
}, timeInMs);
});
return p;
};

runLater(incCounter, 1000).then(() => {
return runLater(incCounter, 2000);
}).then(() => {
return runLater(incCounter, 3000);
});

  • 比起原來的巢狀結構,雖然程式碼變得更多行,但是把任務分得更清楚,更好看得懂也更好維護
Data Structure - Stack & Queue

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×