{%hackmd BJqmTNgxD %}
ASTROCamp 7th JavaScript
===
#### 透過 JavaScript 進行 HTTP 請求 & 非同步
---
### 今天投影片連結
#### [https://ppt.cc/fLqfWx](https://ppt.cc/fLqfWx)
### 隨堂練習包
#### [https://ppt.cc/f514zx](https://ppt.cc/f514zx)
---
## HTTP request via JavaScript
---
### HTTP request
#### [維基百科](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)
#### DevTool 的 Network 可以拿來觀察
---
### `fetch(...)`
##### [MDN 文件](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
---
### 在 `fetch(...)` 出現之前
##### [`$.ajax()`](https://api.jquery.com/jquery.ajax/)
##### [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
---
### 今天為大家準備的 Posts API server
#### [https://pastleo-posts-api.herokuapp.com/](https://pastleo-posts-api.herokuapp.com/)
##### [Git repository](https://github.com/5xTraining/pastleo-js-posts-api)
---
### Web API 常見的傳回格式:[JSON](https://zh.wikipedia.org/wiki/JSON)
```json
[
{
"id": 128,
"title": "hello json!"
}
]
```
---
### `06_fetch-first-post`
##### 使用 `fetch` 抓取並顯示 API 回傳的第一篇文章
---
### `06_fetch-first-post`
```javascript
fetch(...)
.then(request => request.json())
.then(posts => { ... })
```
---
### CORS
#### [Cross-origin resource sharing](https://zh.wikipedia.org/zh-tw/%E8%B7%A8%E4%BE%86%E6%BA%90%E8%B3%87%E6%BA%90%E5%85%B1%E4%BA%AB)
---
### CORS 是怎麼運作的?
##### [設定 CORS headers](https://github.com/5xTraining/pastleo-js-posts-api/blob/bdcb851fb392b20179f9521584d67f35af9d37ec/app/controllers/posts_controller.rb#L99)
##### 一般是沒有 CORS headers
##### [以 Posts 另一組 API 為例](https://pastleo-posts-api.herokuapp.com/posts.json)
---
## Asynchronous / 非同步
---
### 什麼是同步?
```javascript
const two = 1 + 1;
const post = fetchPostSync(); // IO operation
postTitleA.textContent = post.title;
```
---
### 同步的問題

---
### 沒有回應的原因
#### JS 的特性:
* 單執行緒
* [Run-to-completion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#run-to-completion)
#### input/output (IO) operation:
存取網路, 檔案等,通常 CPU 不忙
但是需要等待時間
---
#### 等待的時候可以去處理別的事情
### 這就是 asynchronous 非同步
---
### `07_sync-async-fetch`
#### Callback:
給一個 function 來告知瀏覽器 IO 完成的時候要做什麼
---
### 非同步寫法

---
### Callback hell
<div style='font-size: 18px;'>
```javascript
const xhr1 = new XMLHttpRequest();
xhr1.open('GET', /* ... */);
xhr1.send();
xhr1.addEventListener('load',() => {
const xhr2 = new XMLHttpRequest();
xhr2.open('GET', /* ... */);
xhr2.send();
xhr2.addEventListener('load',() => {
const xhr3 = new XMLHttpRequest();
xhr3.open('GET', /* ... */);
xhr3.send();
xhr3.addEventListener('load',() => {
const xhr4 = new XMLHttpRequest();
xhr4.open('GET', /* ... */);
xhr4.send();
xhr4.addEventListener('load',() => {
// ...
})
})
})
})
```
</div>
---
### 非同步寫法
## [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
---
### Promise
* 你各位已經用過了
* 因為 `fetch` 就回傳 `Promise`
* JavaScript 官方的非同步處理界面
* `.then(callback)`
* 在 resolve 時執行 callback
* `callback` 如果回傳 `Promise`
* **可以繼續串 (chain)**
* `.then(callback).then(callback)`
---
### `08_fetch-first-post-content`
取得文章列表之後,再抓文章內文來顯示
---
### 非同步寫法
## [`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
---
### `async` function
* 寫法
* `async () => {}`
* `async function() {}`
* `async function name() {}`
* 回傳 Promise
* 可用 `await` 關鍵字
* `resolved = await promise`
---
### 改用 `async` / `await`
#### `08_fetch-first-post-content`
---
## http `POST` via JavaScript
#### `fetch(...)` 還有更多參數可用
---
### `09_create_post`
`fetch(url, options)` 的 `options`
* [`url` 與 `posts index` 相同](https://pastleo-posts-api.herokuapp.com/api/posts)
* `method: 'POST'`
* `headers: { Authorization: '...' }`
* `pastleo-js-posts-api-secret`
* `body: new FormData(form)`
* [FormData](https://developer.mozilla.org/zh-TW/docs/Web/API/FormData)
---
### `09_create_post`
* 錯誤處理
* `response.ok`
* 顯示成功/錯誤訊息
* Loading 效果
* 防止重複送出
* 成功後清除輸入
---
### `new Promise(...)`
#### 回家作業:
<div style='font-size: 24px;'>
完成 `09_create-post`,並且把 `setTimeout` 包裝成 `Promise`
使得最後重設送出按鈕狀態的程式可以這樣改寫:
```diff
-setTimeout(() => {
- submitBtn.textContent = 'Submit';
- submitBtn.disabled = false;
-}, 2000);
+await timeoutPromise(2000);
+submitBtn.textContent = 'Submit'
+submitBtn.disabled = false;
```
[MDN 的 Promise Constructor 範例](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise)
</div>
---
## Recap
* `fetch(...)`
* CORS
* sync/async: callback
* Promise
* `async` / `await`
{"metaMigratedAt":"2023-06-15T22:39:56.570Z","metaMigratedFrom":"YAML","title":"透過 JavaScript 進行 HTTP 請求 & 非同步 - ASTROCamp 7th JavaScript","breaks":true,"slideOptions":"{\"spotlight\":{\"enabled\":true,\"size\":80,\"presentingCursor\":\"default\",\"toggleSpotlightOnMouseDown\":false,\"spotlightOnKeyPressAndHold\":90,\"initialPresentationMode\":true,\"disablingUserSelect\":false,\"fadeInAndOut\":500}}","contributors":"[{\"id\":\"0eb274f7-a3a4-4c8e-b0d4-e5c08eaf9e72\",\"add\":6465,\"del\":1247}]"}