async/await封裝fetch

基本操做

一個基本的fetch操做很簡單。就是經過fetch請求,返回一個promise對象,而後在promise對象的then方法裏面用fetch的response.json()等方法進行解析數據,因爲這個解析返回的也是一個promise對象,因此須要兩個then才能獲得咱們須要的json數據。es6

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

爲什麼不能直接使用基本操做

fetch規範與jQuery.ajax()主要有兩種方式的不一樣:
一、當接收到一個表明錯誤的 HTTP 狀態碼時,好比400, 500,fetch不會把promise標記爲reject, 而是標記爲resolve,僅當網絡故障時或請求被阻止時,纔會標記爲 reject。
二、默認狀況下,fetch 不會從服務端發送或接收任何 cookies, 若是站點依賴於用戶 session,則會致使未經認證的請求(要發送 cookies,必須設置 credentials 選項)。ajax

從這裏能夠看出來,若是咱們要在fetch請求出錯的時候及時地捕獲錯誤,是須要對response的狀態碼進行解析的。又因爲fetch返回的數據不必定是json格式,咱們能夠從header裏面Content-Type獲取返回的數據類型,進而使用正確的解析方法。json

使用async/awiait的緣由

Promise 將異步操做規範化.使用then鏈接, 使用catch捕獲錯誤, 堪稱完美, 美中不足的是, then和catch中傳遞的依然是回調函數, 與心目中的同步代碼不是一個套路.api

爲此, ES7 提供了更標準的解決方案 — async/await. async/await 幾乎沒有引入新的語法, 表面上看起來, 它就和alert同樣易用。跨域

var word = '123',
    url = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+word+'&json=1&p=3';
(async ()=>{
  try {
    let res = await fetch(url, {mode: 'no-cors'});//等待fetch被resolve()後才能繼續執行
    console.log(res);//fetch正常返回後才執行
    return res;//這樣就能返回res不用擔憂異步的問題啦啦啦
  } catch(e) {
    console.log(e);
  }
})();

代碼

解析結果值

檢查返回值的狀態: 上面提到了,由於fetch不會本身reject,因此咱們只可以經過拋出錯誤幫一下它啦。301和302是重定向的狀態碼,這個時候頁面須要跳轉一下,經過window.location實現是否是很perfer呢。promise

checkStatus(response) {//檢查響應狀態
        if(response.status >= 200 && response.status < 300) {//響應成功
            return response;
        }
        if(response.status === 301 || response.status === 302) {//重定向
            window.location = response.headers.get('Location');
        }
        const error = new Error(response.statusText);
        error.data = response;
        throw error;
    }

判斷用哪一個fetch的解析函數:這裏經過headers的Content-Type判斷使用哪一個解析方法,由於解析也是異步的,因此仍是用async/await讓程序停在那裏慢慢解析。cookie

async parseResult(response) {//解析返回的結果
        const contentType = response.headers.get('Content-Type');
        if(contentType != null) {
            if(contentType.indexOf('text') > -1) {
                return await response.text()
            }
            if(contentType.indexOf('form') > -1) {
                return await response.formData();
            }
            if(contentType.indexOf('video') > -1) {
                return await response.blob();
            }
            if(contentType.indexOf('json') > -1) {
                return await response.json();
            }
        }
        return await response.text();
    }

爲了調用比較好看吧,寫多一個processResult去調用者兩個方法,而後在fetch的then裏面就只須要用這個去獲得結果啦。網絡

async processResult(response) {
        let _response = this.checkStatus(response)
        _response = await this.parseResult(_response);
        return _response;
    }
fetch請求後臺代碼

把請求後臺的代碼都寫在_request裏面,而後get和post裏面就封裝一下參數。session

async _request(url, init, headers = {}) {
        try {
            let options = _.assign(
                    {
                        credentials: 'include',//容許跨域
                    },
                    init
            );
            options.headers = Object.assign({}, options.headers || {}, headers || {});
            let response = await fetch(url, options);
            response = await this.processResult(response);//這裏是對結果進行處理。包括判斷響應狀態和根據response的類型解析結果
            return response;
        } catch(error) {
            throw error;
            return null;
        }
    }

    async get(api, data = {}, headers = {}, config = {}) {
        const query = _.isEmpty(data) ? '' : `json=${encodeURIComponent(JSON.stringify(data))}`;
        return await this._request(`${api}?${query}`, headers, {}, config);
    }

    async post(api, data = {}, headers = {}, config = {}) {//經過headers決定要解析的類型
        const _headers = {
            'Content-Type': 'application/x-www-form-urlencoded',
            ...headers,
        };
        let formBody = null;
        if(_headers['Content-Type'] && _headers['Content-Type'].indexOf('application/x-www-form-urlencoded')>-1) {
            formBody = new URLSearchParams();
            for(let k in data) {//遍歷一個對象
                if(typeof(data[k]) === 'object') {
                    formBody.append(k, JSON.stringify(data[k]));
                } else {
                    formBody.append(k, data[k]);
                }
            }
        }
        return await this._request(
                api,
                {
                    method: 'POST',
                    headers: _headers,
                    body: formBody,
                },
                {},
                config,
        )
    }

how to use

把上面這些代碼到寫在一個http類裏面app

import 'isomorphic-fetch'
    import 'es6-promise'
    import _ from 'lodash';
    class http {
         checkStatus(response) {}
         async parseResult(response) {}
         async processResult(response) {}
         async _request(url, init, headers = {}) {}
         async get(api, data = {}, headers = {}, config = {}) {}
         async post(api, data = {}, headers = {}, config = {}) {}
     }  
       
    let http = new Http();
    export default http;

而後調用的時候

import http from '../../common/http'
    getData() {
        //form類型
        http.post('/api/submitComment', {a: 'hhhh'}).then((data) => {
            console.log(data);//輸出返回的數據
        })
        //json類型
        http.post('/api/submitComment', {a: 'hhhh'}, {'content-type': 'application/json'
        }).then((data) => {
            console.log(data);//輸出返回的數據
        })
    }
相關文章
相關標籤/搜索