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