---
# System prepended metadata

title: 【js筆記】非同步 & Promise
tags: [JAVASCRIPT]

---

---
tags: JAVASCRIPT
---
【js筆記】非同步 & Promise
===
### Ajax
#### method
  - get 取得
  - post　新增（安全性較get高）
  - put 更新
  - delete 刪除
### 非同步常見問題
- 回呼問題
- 寫法不一致，多層巢狀，波動拳問題
- 無法同時執行(jQuery有並行寫法，但不直覺)
```javascript
const url = "https://randomuser.me/api/";
$.ajax({
    url: url
}).done(function(res) {
    const seed = res.info.seed;
    console.log(seed, res)
    $.ajax({
        url: `${url}?seed=${seed}`
    }).done(function(res2) {
        console.log(res2)
    });
});
```

# Promise
Promise 是一個表示非同步運算的最終完成或失敗的物件
```javascript
promise.then(successCallback, failureCallback);
```
- 寫法一致
  - 都用 then 
- then，return　axios.get(...)，減少多層巢狀
- all，同時發出請求，確保都完成時才執行結果
```javascript
Promise.all([axios.get(url), axios.get(url)])
.then(([res1, res2])=>{
    console.log(res1, res2);
});
```

## 創立自己的promise

```javascript
 function promiseFn(num) {
    return new Promise((resolve, reject)=>{
        setTimeout(() => {
            if(num) {
                resolve(`成功 ${num}`);
            } else {
                reject('失敗');
            }
        }, 10);
    });
}
promiseFn(1)
.then( res => {
    console.log(res);
    return promiseFn(0);
})
.then( res => {
    console.log(res);
    return promiseFn(3);
})
.then(res => {
    console.log(res)
})
.catch(res=>{
    console.log('catch', res);
    return promiseFn(4)
})
.then(res => {
    console.log(res)
})
console.log('程式碼end');
```
## 可直接使用then來接收"成功"及"失敗"
```javascript
promiseFn(0)
.then( (res)=> {
    console.log('success', res);
}, (res) => {
    console.log('fail', res);
})
```

## Promise 的 chain, 串接
有個常見的需求是依序呼叫兩個以上的非同步函數，我們稱之為建立 Promise 鏈。
```javascript
promiseFn(1)
.then( res => {
    console.log(res);
    return promiseFn(2);
})
.then( res => {
    console.log(res);
})
```
## Promise常見方法
#### all
- 回傳array
- 其中一個失敗者失數
- all 很常用：對遠端作出請求，發出多個api，當api都做完才進行下個步驟
```javascript
Promise.all([
    promiseFn(1, 500),
    promiseFn(2, 1000),
    promiseFn(3, 3000),

])
.then((res)=>{
    console.log(res); //傳回array
    console.log(res[0], res[1], res[2])
})
.catch((rej)=>{
    console.log(rej)
})
```
#### race
- 回傳第一個執行完的結果
- 非第一個執行完的有失敗，就不會失數
```javascript
Promise.race([
    promiseFn(0, 500),
    promiseFn(2, 20),
    promiseFn(3, 3000),

])
.then((res)=>{
    console.log(res); //傳回第一個完成結果
})
.catch((rej)=>{
    console.log(rej) //　第一個完成的出錯才會進到這裡
});
```
## promise & ajax結合
### XMLHttpRequest
[XMR說明](https://developer.mozilla.org/zh-TW/docs/Web/API/XMLHttpRequest)
- 藉由 XMLHttpRequest（XHR）物件的方式來存取伺服器端的資料，可以讓你直接經由指定的 URL 擷取資料卻不用刷新整個網頁。
- 不好管理
```javascript
const url = "https://jsonplaceholder.typicode.com/todos/1";

var req = new XMLHttpRequest();
//定義方法
req.open('GET', url);
req.onload = function() {
    if(req.status = 200) {
        console.log(req.response);
    } else {
        // 失敗的話
    }
};
req.send();
```
- 透過promise來實行
```javascript
        function get(url) {
            return new Promise((resolve, reject) => {
                var req = new XMLHttpRequest();
                //定義方法
                req.open('GET', url);
                req.onload = function() {
                    if(req.status = 200) {
                        resolve(req.response);
                    } else {
                        // 失敗的話
                        reject(req)
                    }
                };
                req.send();
            });
        }

        get(url)
            .then(res => {
                console.log('get ', res);
                return get(url);
            })
            .then(res => {
                console.log('get2 ', res);
            })
            .catch(err => {
                console.log(err)
            })
```
# Axios
- 依賴 ES6 Promise，若不支援請使用 Polyfill
- 支援 Promise API
- 可取消請求 (Promise 無法)
- 自動轉換 JSON

```javascript
axios.get(url)
.then((res)=>{
    console.log(1, res);
    const seed = res.data.info.seed;
    return axios.get(`${url}?seed=${seed}`)
})
.then((res)=>{
    // 接收上一個then的return值
    console.log(2, res);
})
```
