# Fetch & Promise
###### tags: `Lidemy`
工作後,也一段時間沒好好做筆記,因為打算使用 Node.js 的框架 Moleculer,因此來複習一下 Fetch 跟 Promise :p
首先,要在前端網頁發 **Ajax**(Asynchronous JavaScript and XML)的話,不外乎兩個方法:
1. XMLHttpRequest (XHR)
2. Fetch (也就是今天的主角)
免費的串 API 網站:https://designer.mocky.io/
## Fetch (一) : 來 Get 個 Promise
GET的方式 : `fetch('網址')`
沒錯,就這樣
用變數去接的話,可以來看看回傳是什麼:
```javascript
const response = fetch('網址')
console.log(response) // Promise {...}
```
是的,fetch 會回傳一個叫 "Promise" 的獨特物件
> 要拿到 Promise 的結果,就需使用 `.then()` 來接,而`.then()` 裡要放個 callback function
```javascript
function getResult(x) {
console.log(x) // 會回傳 response
}
const result = fetch('網址')
result.then(getResult)
```
來簡化一下
```javascript
function getResult(x) {
console.log(x) // 會回傳 response
}
fetch('網址').then(getResult)
```
更簡化,改成匿名函數 (anonymous function)
```javascript
fetch('網址')
.then( response => {
console.log(response)
})
```
那 response 就可以直接引用了嗎?非也
我們通常要拿到的是 response 的內容,而這個內容無法直接取得,這邊舉例兩個常用的取用方式:
1. `.text()`
2. `.json()` (過程中已自動做 `Json.Parse(response)` 的動作)
而上面的兩個 function,回傳的也是 promise,因此再此複習前面所說:
> 要拿到 Promise 的結果,就需使用 `.then()` 來接,而`.then()` 裡要放個 callback function
>
```javascript
fetch('網址')
.then( response => {
response.json().then.(content => {
console.log(content)
})
})
```
而 Promise 也能搭配 return 使用,return 的值將會是對上一個 promise 回傳值,return 回傳什麼,下面的 `.then()` 就接什麼
```javascript
fetch('網址')
.then(response => {
return response.json()
}).then(finalResult => {
console.log(finalResult)
})
```
上面的例子,`finalResult` 所回傳的就是`response.json()` 的值,上面曾提到,`.json()`會自動解析json格式,所以最終回傳值就會是一個 js 可使用的 Object。而使用 return 則是可以增加可讀性,例如要取得某電腦的資訊:
```
// 為說明例子,非實際程式碼
getComputer(() => {
return getBrand()
}).then(brands => {
return getMotherBoard
}).then(motherBoard => {
return getPrice()
})
```
## Fetch (二): 錯誤處理 & POST
這邊的錯誤是指無法拿到 response,而非 HTTP status code 回傳的 500 or 404 錯誤。
基本方法:加入 `catch()`
```javascript
fetch('網址')
.then(res => {
return res.text()
}).then(text => {
console.log(text)
}).catch(error => {
console.log(error)
})
```
Post 方式,加入 method, body, headers
引用 MDN 資料+
```
fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data), // data can be `string` or {object}!
headers: new Headers({
'Content-Type': 'application/json'
})
}).then()
```
- 注意 headers 中的 Content-type,要跟 body 的內容相對應,否則 Server 可能就出錯惹
- 注意 CORS,若非同源的 Domain,可能要帶上 cookie,需要在加上 credentials 這個選項
```
fetch(url, {
credential: 'XXX'
})
```
- 注意 `mode: 'no-cors'` 並不能突破同源政策限制,只是跟瀏覽器說 Server 無提供 Cors header,網址也不預期要接收 repsonse,不用回傳錯誤。
## Promise (一) DIY 及 簡化
來試著自行產生一個promise
```
function init(resolve, reject) {
resolve("heyhey")
}
const promise = new Promise(init)
DIYpromise.then((data) => {
console.log('data:', data)
})
```
簡化一下 (常看到的 Promise):
```
const promise = new Promise((resolve, reject) => {
...
// request 成功傳
if (status >= 200 && status < 400) resolve("data")
...
// request error 傳
if (err) => { reject(err) }
})
```
想要回傳什麼值放resolve,錯誤回傳什麼放 reject
另一種 Promise,非同步發送
```
const promise2 = new Promise(resolve => {
setTimeout(resolve, 3000)
})
```
用 function 包起來:
```javascript
function delay(ms) {
const promise2 = new Promise(resolve => {
setTimeout(resolve, ms)
})
return promise2
}
delay(2000).then((data) => {
console.log('promise data:', data)
})
```
簡化 promise 變成:
```javascript
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
```
搭配匿名函數再度簡化:
```javascript
const delay = ms => new Promise(resolve => {
setTimeout(resolve, ms)
})
```
再再再簡化:
```javascript
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
```
### Async / Await
> 讓看起來同步的語法,背後用非同步的方式運行
await 後面接一個 promise,等該 promise 執行完,才會執行下一行
```
async function main() {
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
async function main() {
console.log('enter main')
await delay(2000)
console.log('exit main')
}
main()
}
```