[JS] 非同步系列:Promise 簡介

Promise 是 ES6 眾多好用方法之一,能用簡潔的撰寫方式處理非同步事件,大幅優化 callback hell 造成閱讀困難的問題。

Promise

Promise 是一個建構函式,帶有兩個參數:resolve、reject,分別是用來處理執行成功和失敗時的回傳資訊,格式如下:

1
2
3
4
5
6
7
8
9
10
11
const doSomething = new Promise((resolve, reject) => {
// 使用 random 隨機產生 true 或 false 來回傳不同的結果
let result = Boolean(Math.round(Math.random()));
if (result) {
// 使用 resolve 回傳成功時的資料
resolve(`Success`);
} else {
// 使用 reject 回傳失敗時的資料
reject(`Failed`);
}
});

Promise (.then .catch)

new Promise() 建立的 Promise 物件提供 .then 和 .catch 兩個方法來分別接收 resolve 和 reject 回傳的資料,撰寫格式如下:

1
2
3
4
5
6
7
8
9
doSomething
.then((data) => {
// .then 接收 成功(resolve) 的回傳值
console.log(data);
})
.catch((error) => {
// .catch 接收 失敗(reject) 的回傳值
console.log(error);
});

Promise + 自訂參數

Promise 不能直接傳入參數,但是可以使用函式建立參數的接口,再回傳 Promise 物件,就能加入其他的參數了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const doSomething = (name) => {
// return 一個 Promise 物件
return new Promise((resolve, reject) => {
let result = Boolean(Math.round(Math.random()));
if (result) {
resolve(`${name}, success!`);
} else {
reject(`${name}, failed!`);
}
});
};

doSomething('Peter')
.then((data) => console.log(data))
.catch((error) => console.log(error));

// 成功(resolve)時輸出:Peter, success!
// 失敗(reject)時輸出:Peter, failed!

串接多個 Promise

Promise 的 then 方法用串接的方式連接下個行為,不像 callback function 需要槽狀層層包覆。

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
// 加入第二個會隨機決定成功或失敗的函式
const doSomethingElse = (str) => {
return new Promise((resolve, reject) => {
let result = Boolean(Math.round(Math.random()));
if (result) {
resolve(`Good job, ${str}`);
} else {
reject(`Bad job, ${str}`);
}
});
};

// 呼叫第一個函式
doSomething('Peter')
.then((result) => {
// 將第一個函式的執行結果傳入第二個函式
doSomethingElse(result);
})
.then((result) => {
// 顯示第二個函式的執行結果
console.log(result);
})
.catch((error) => {
// 不管是第幾個函式,出錯就會在這邊被捕捉
console.log(error);
});

// doSomething 成功、doSomethingElse 成功時輸出:Good job, Peter, success!
// doSomething 失敗時輸出:Peter, failed!
// doSomething 成功、doSomethingElse 失敗時輸出:Bad job, Peter, failed!

Promise.all

當有不同的資料需要合併處理時使用,只要其中一項失敗就會立刻回傳失敗,不會等其他的非同步跑完,成功時則是以陣列方式回傳資料。

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
const doSomething = (name) => {
return new Promise((resolve, reject) => {
let result = Boolean(Math.round(Math.random()));
if (result) {
resolve(name);
} else {
reject(`${name}, failed`);
}
});
};

Promise.all([
doSomething('Peter'),
doSomething('Parker'),
doSomething('Porker'),
])
.then((data) => {
console.log(`恭喜 ${data.join('、')} 全部成功!`);
})
.catch((error) => {
console.log(error);
});

// 全部成功時顯示:恭喜 Peter、Parker、Porker 全部成功!
// 任一個失敗時顯示: 傳入的名稱, failed