JavaScript 非同步與同步事件之 Promise

前言

async 與 await 是 JavaScript 中常見的東西,其中也與 Promise 也有關係,所以這邊也會先講一下 Promise 是什麼。

非同步與同步事件

首先到底非同步與同步事件是什麼東西呢?我們在 JavaScript 奇怪的部分有瞭解到 JavaScript 是單進程的程式語言(意旨一次只能做一件事情)

同步事件

舉例來講 JavaScript 就像下廚差不多,以同步 (async) 來講就像我要一定要先切菜 → 再切香菇 → 然後倒高湯 → 最後一定是上菜。

同步事件

非同步事件

那非同步呢?做菜過程除了最後上菜之外,難道我們就一定要先切菜 → 再切香菇 → 然後倒高湯嗎?不一定,我們可以先切香菇或者先到高湯又或者在最後才切菜等等,簡單來說就是我們可以先做其他可以先做的事情,然後其他事情不會受到影響。

那剛剛前面有講到 JavaScript 是單進程程式語言,那他又是如何處理非同步事件的?在 JavaScript 中,他會先將應該要先處理的東西先處理完,然後再把需要做非同步處理的事情放進一個叫做事件佇(ㄓㄨˋ)列,又稱事件對列中,直到前面應該先處理完的事情處理完後再看事件佇列有什麼是準備要做的。

Promise

由於 JavaScript 中有非常多的非同步事件,所以就會有一個東西叫做 Callback Hell (回呼地獄),當我們希望這件事情可以處理之後在往下處理的時候就會這個恐怖的 Callback Hell。

Callback Hell

那 Promise 該如何撰寫呢?

首先 Promise 是 ES6 新增的一個功能,所以必須使用函式來呼叫,常見的寫法就像這樣 ↓

1
2
3
4
let newPromise = new Promise((resolve, reject) => {
// resolve → 代表操作成功,並將值回傳回去
// reject → 代表失敗,回傳錯誤訊息
})

一開始我們無法知道哪一個函式會先被執行完,但是我們又希望他可以照順序來運作,那就可以這樣子寫 ↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let runPromiss = (name, time, status) => {
return new Promise((resolve, reject) => {
if(status) {
console.log(`${name} 起跑了!`)
return setTimeout(() => {
resolve(`${name} 衝了 ${time / 1000}秒`)
},time);
} else {
reject(new Error(`${name} 出現錯誤`));
};
});
};
// 小明失敗,就回傳錯誤訊息
runPromiss('小明', 3000, false).then((res) => {
console.log(res);
return runPromiss('小王', 1000, true)
}).then((res) => {
console.log(res);
}).catch((error) => {
console.log(error);
})

從上面這個狀況下我們可以看到小明失敗之後直接回傳錯誤訊息。

小明失敗了

可是如果今天小明是成功跑完,那接下來我們就可以看到接著換小王跑起來了。

小王跑起來!!

小注意事項

所以我們就可以使用這種方式來處理非同步事件,這邊有一個事情一定要知道,如果 Promise 沒有回傳 resolve,那就不會接下去 then 繼續運作唷,如下 ↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let runPromiss = (name, time, status) => {
return new Promise((resolve, reject) => {
if(status) {
console.log(`${name} 起跑了!`)
return setTimeout(() => {
console.log(`${name} 衝了 ${time / 1000} 秒`)
},time);
} else {
reject(new Error(`${name} 出現錯誤`));
};
});
};
// 小明失敗,就回傳錯誤訊息
runPromiss('小明', 3000, true).then((res) => {
console.log(res);
return runPromiss('小王', 1000, true)
}).then((res) => {
console.log(res);
}).catch((error) => {
console.log(error);
})

沒有 resolve

更多 Promise 介紹

因為 Prmoise 做法還滿多的,所以這邊提供還有那些文章。

Day23-非同步實作篇 II!Promise

重新認識 JavaScript: Day 26 同步與非同步

鐵人賽:使用 Promise 處理非同步

Callback Hell

順便科普 Callback Hell 會造成什麼影響。

剛剛前面有講到,當我們希望 JavaScript 在運作程式碼的時候,可以依照我們所希望的事件來依序處理,可是如果沒有使用 ES6 Promise 的時候,就很常會看到一推 if…if…判斷式來判斷資料是否已經正常取的或者處理完畢,當然我自己也有寫過這種 Callback Hell 在 debug 上是非常痛苦的,因為括號非常的多,會搞不清楚哪一個是哪一個的結尾區塊。

RagnarokShopV3 作品其中一段 Callback Hell

後來改用 Promise 之後就可以發現其實好 debug 多了(其實這部分還可以優化,最近有點忙)。

RagnarokShopV3 作品其中一段

0%