---
# System prepended metadata

title: Fetch & Promise
tags: [Lidemy]

---

# 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()
}
```