從 fetch 來認識 Promise

Promise 最核心的地方就放在這篇了。

fetch 回傳的是一個 Promise

1
2
3
4
const url = 'https://api.com';
const res = fetch(url);
// Promise 物件
console.log(res)

這就跟 $('.btn') 會回傳 jQuery 物件,new Date() 會回傳 Date 物件,/yo/ 會回傳 RegExp 物件是一樣的道理。fetch 也會回傳一個物件,這個物件就叫做 Promise

但要注意並不是只有 fetch 才會回傳 Promise,fetch 只是其中之一而已。

例如以下回傳的也是 Promise:

1
2
3
4
// 讀取存在剪貼簿的值(Ctrl+C)
navigator.clipboard.readText().then(text => {
console.log(text)
});

怎麼從 Promise 物件拿到結果

要用 Promise 提供的 .then 方法來取得,裡面傳的是一個 callback function:

1
2
3
4
5
const url = 'https://api.com';
fetch(url)
.then(function (data) {
console.log(data);
});

結果大概長這樣:

1
2
3
4
5
6
7
8
9
Response {
type: 'cors',
url: 'https://run.mocky.io/v3/ec9680e0-f534-40a1-bbc4-5ef69fd61cc6',
redirected: false,
status: 200, ok: true,
body: (...)
bodyUsed: false
headers: Headers {}
}

這時候就會很疑惑「居然沒辦法看到 body?」,這是因為從 then 裡面拿到的東西也是一個物件,我不確定它實際上叫做什麼,但總之先叫它 Response 吧。

如果要看到 body 的內容的話得用 Response 物件提供的方法,最常見的有兩種:

  • response.text()
  • response.json()

結果這樣寫之後又會發現:「靠腰,怎麼又是 Promise」

1
2
3
4
5
fetch(url)
.then(function (response) {
// Promise 物件
console.log(response.text());
});

沒錯,這就是 Promise 的套路,通常在 Promise 裡會再回傳另一個新的 Promise。

但是不用想太複雜,這個就跟 jQuery 可以一直 $(.btn).find('.aa').css() 的道理有點像。為什麼可以一直這樣 . 下去?因為每一次的回傳值都是 jQuery 物件。

所以如果要再從 Promise 裡面拿到結果,就要再用 then 來拿:

1
2
3
4
5
fetch(url)
.then((response) => {
// 這邊就真的拿的到 body 了
response.text().then(body => console.log(body));
});

為什麼可以 then 再接 then

因為 then 這個方法回傳的也是一個 Promise…

而且在 then 裡面「回傳的東西」會是「下一個 then 裡面會拿到的值」。這一段很重要,所以麻煩記起來,拜託拜託。

以上面的例子來示範:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fetch(api500)
.then((response) => {
response.json()
.then(body => {
console.log(body);
// 在這回傳東西
return '123';
})
// 這邊就能拿到剛剛回傳的東西
.then(fromFirst => {
// 123
console.log(fromFirst);
// 這邊不回傳東西
})
.then(fromSecond => {
// 拿到預設回傳的 undefined
console.log(fromSecond)
})
});

再來是另外一個重點,如果在 then 裡回傳的也是 Promise那下一個 then 拿到的會是Promise 解析後(做完 then 後)的值

一樣示範一次:

1
2
3
4
5
6
7
8
9
10
fetch(url)
.then(response => {
// response.json() => Promise
return response.json();
})
.then(json => {
// 這裡拿到的值等於:
// response.json().then(json => json)
console.log(json)
});

這個就是 Promise 最大的優勢了,為什麼?你比較看看這兩段程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第一個
fetch(url)
.then(response => {
response.json()
.then(json => {
console.log(json);
});
});
// 第二個
fetch(url)
.then(response => {
return response.json();
})
.then(json => {
console.log(json);
})

第一個寫法根本就是 callback hell,但第二個寫法就改善這問題了,因為它把層數壓低了。

可是為什麼能這樣子?原理就是剛剛講的,如果在 then 裡面回傳 Promise 的話,下一個 then 就會拿到解析後的結果。

這點還蠻有趣的,網路上確實只會教你第二種寫法,但很少人告訴過你為什麼可以這樣子。總之,相信現在你應該能理解了。

來做個總結

複習一下 Promise 的幾個特性:

  1. 要拿到 Promise 的結果要用 then
  2. then 本身會回傳 Promise,所以可以一直 then 下去
  3. then 裡面回傳什麼,下個 then 就會拿到什麼
  4. 如果 then 裡回傳的也是 Promise,下個 then 拿到的就會是 Promise 解析後的值
你不知道的 console 小技巧 mentor-program-day79
Your browser is out-of-date!

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

×