16

基本操作

一个基本的fetch操作很简单。就是通过fetch请求,返回一个promise对象,然后在promise对象的then方法里面用fetch的response.json()等方法进行解析数据,由于这个解析返回的也是一个promise对象,所以需要两个then才能得到我们需要的json数据。

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

为何不能直接使用基本操作

fetch规范与jQuery.ajax()主要有两种方式的不同:
1、当接收到一个代表错误的 HTTP 状态码时,比如400, 500,fetch不会把promise标记为reject, 而是标记为resolve,仅当网络故障时或请求被阻止时,才会标记为 reject。
2、默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。

从这里可以看出来,如果我们要在fetch请求出错的时候及时地捕获错误,是需要对response的状态码进行解析的。又由于fetch返回的数据不一定是json格式,我们可以从header里面Content-Type获取返回的数据类型,进而使用正确的解析方法。

使用async/awiait的原因

Promise 将异步操作规范化.使用then连接, 使用catch捕获错误, 堪称完美, 美中不足的是, then和catch中传递的依然是回调函数, 与心目中的同步代码不是一个套路.

为此, 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呢。

    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让程序停在那里慢慢解析。

    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里面就封装一下参数。

    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类里面

    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);//输出返回的数据
        })
    }

supportlss
230 声望16 粉丝