XMLHttpRequest是一个设计粗糙的API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式很是混乱,并且基于事件的异步模型写起来也没有现代的Promise,generator/yield,async/await友好。es6
Fetch的出现就是为了解决XHR的问题。
传统使用XHR发送一个json请求通常是这样ajax
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后json
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捕获promise
Fetch优势:
1. 语法简洁,更加语义化
2. 基于标准Promise实现,支持 async/await
3. 同构方便,使用 isomorphic-fetch浏览器
Fetch polyfill的基本原理是探测是否存在 window.fetch 方法,若是没有则用XHR实现。有些浏览器(Chrome 45)原生支持Fetch,但响应中有中文时会乱码。(可以使用fetch-detector和fetch-ie8)服务器
fetch(url, {credentials: 'include'})
由于fetch返回promise致使码,在某些错误的http状态下如400、500等不会reject,相反会被resolve;只有网络错误会致使请求不能完成时,fetch才会被reject;因此通常会对fetch请求作一层封装,以下:cookie
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不像大多数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实例数组参数,表示多个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]) }