• Fetch


    Why Fetch

    XMLHttpRequest是一个设计粗糙的API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,而且基于事件的异步模型写起来也没有现代的Promise,generator/yield,async/await友好。

    Fetch的出现就是为了解决XHR的问题。
    传统使用XHR发送一个json请求一般是这样

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = 'json';
    
    xhr.onload = function () {
        console.log(xhr.response);
    }
    xhr.onerror = function () {
        console.log('Oops, error');
    }
    xhr.send();
    

    使用Fetch后

    fetch(url).then(function (response) {
        return response.json();
    }).then(function (data) {
        console.log(data);
    }).catch(function (e) {
        console.log('Oops, error')
    })
    

    使用ES6的箭头函数后:

    fetch(url).then(response => response.json())
        .then(data => console.log(data))
        .catch(e => console.log('Oops, error', e))
    

    但这种Promise的写法还是有callback的影子,而且promise使用catch方法进行错误处理有点奇怪。
    使用async/await进行优化(属于ES7)

    try {
        let response = await fetch(url);
        let data = response.json();
        console.log(data);
    } catch (e) {
        console.log('Oops, error', e);
    }
    
    // 注:这段代码如果想运行,外面需要包一个async function
    

    使用await后,写异步代码就像写同步代码一样。await后面可以跟Promise对象,表示等待Promise的resolve()才继续向下执行,如果Promise被reject()或抛出异常则被外面的try...catch捕获

    Fetch优点:
    1. 语法简洁,更加语义化
    2. 基于标准Promise实现,支持 async/await
    3. 同构方便,使用 isomorphic-fetch

    Fetch启用方法

    1. 由于 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
    2. 引入 Promise 的 polyfill: es6-promise
    3. 引入 fetch 探测库:fetch-detector
    4. 引入 fetch 的 polyfill: fetch-ie8
    5. 可选:如果你还使用了 jsonp,引入 fetch-jsonp
    6. 可选:开启 Babel 的 runtime 模式,现在就使用 async/await

    Fetch polyfill的基本原理是探测是否存在 window.fetch 方法,如果没有则用XHR实现。有些浏览器(Chrome 45)原生支持Fetch,但响应中有中文时会乱码。(可使用fetch-detector和fetch-ie8)

    Fetch常见坑

    • Fetch请求默认不带cookie的,需要设置fetch(url, {credentials: 'include'})
    • 服务器返回400、500错误码并不会reject,只有网络错误这些导致请求不能完成时,fetch才会被reject
    • IE8、9的XHR不支持CORS跨域,虽然提供XDomainRequest,但这个东西不支持传cookie,所以必要时还是使用jsonp,推荐使用fetch-jsonp

    fetch请求对某些错误http状态不会reject

    因为fetch返回promise导致码,在某些错误的http状态下如400、500等不会reject,相反会被resolve;只有网络错误会导致请求不能完成时,fetch才会被reject;所以一般会对fetch请求做一层封装,如下:

    function checkStatus (response) {
        if ( response.status >= 200 && response.status < 300) {
            return response;
        }
        const error = new Error (response.statusText);
        error.response = response;
        throw error;
    }
    function parseJSON (response) {
        return response.json();
    }
    export default function request (url, options) {
        let opt = options || {};
        return fetch (url, {credentials : 'include', ...opt})
            .then (checkStatus)
            .then (parseJSON)
            .then (data => data)
            .catch (err => err)
    }
    

    fetch不支持超时timeout处理

    fetch不像大多数ajax库可以对请求设置超时timeout,所以在fetch标准添加超时feature之前,都需要polyfill该特性。
    我们真正需要的是abort(), timeout可以通过timeout+abort方式实现,起到真正超时丢弃当前的请求。

    实现fetch的timeout功能,思想就是新创建一个可以手动控制promise状态的实例,对于不同状态来对新创建的promise进行resolve或者reject,从而达到实现timeout功能。

    方法一

    var oldFetchfn = fetch; // 拦截原始的fetch方法
    window.fetch = function (input, opts) {
        return new Promise ( function (resolve, reject) {
            var timeoutId = setTimeout (function () {
                reject (new Error ('fetch timeout'))
            }, opts.timeout);
        oldFetchfn (input, opts).then(
            res => {
                clearTimeout (timeoutId);
                resolve (res);
            },
            err => {
                clearTimeout (timeoutId);
                reject (err);
            }
        )
        })
    }
    

    模拟XHR的abort功能:

    var oldFetchfn = fetch;
    window.fetch = function (input, opts) {
        return new Promise ( function (resolve, reject) {
            var abort_promise = function () {
                reject (new Error ('fetch abort'))
            };
           var p = oldFetchfn (input, opts).then (resolve, reject);
            p.abort = abort_promise;
            return p;
        })
    }
    

    方法二: 利用Promise.race方法

    Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变。

    var oldFetchfn = fetch; // 拦截原始的fetch方法
    window.fetch = function (input, opts) {
        var fetchPromise = oldFetchfn (input, opts);
        var timeoutPromise = new Promise ( function (resolve, reject) {
            setTimeout ( () => {
                reject (new Error ('fetch timeout'))
            }, opts.timeout)
        });
        return Promise.race([fetchPromise, timeoutPromise])
    }
    
  • 相关阅读:
    glBlendFunc的几种常用情况
    android发布版本的几个命令
    android拾遗——Android 动画学习笔记
    android拾遗——四大基本组件介绍与生命周期
    C++拾遗——重新开始
    mark Java NIO
    转 mysql中int、bigint、smallint 和 tinyint的区别与长度的含义
    mysql到redis的复制
    MySQL UDF(自定义函数)
    windows下redis 和 hiredis的编译与使用
  • 原文地址:https://www.cnblogs.com/douglasvegas/p/7365321.html
Copyright © 2020-2023  润新知