新澳门萄京娱乐场官网XMLHttpRequest和Fetch API,您认为哪种最适合Ajax?-Web前端之家

新澳门萄京娱乐场官网 2

来台湾出差快一个星期了,相比大陆深圳,还是更喜欢内地多点,这边的天气实在不敢恭维,天天下雨,交通汽车尾气太重了,摩托车四处穿插…..休息时间还是呆在家里,写写东西,给大家分享点干货,今天来聊点数据ajax的基础知识。

新澳门萄京娱乐场官网 1

2019年3月庆祝Ajax成立20周年。有点。XMLHttpRequest作为IE5.0
ActiveX组件于1999年交付的第一个实现。

原谅我做一次标题党,Ajax 不会死,传统 Ajax 指的是
XMLHttpRequest(XHR),未来现在已被 Fetch 替代。

在此之前,曾经有一些方法可以在不刷新整个页面的情况下从服务器提取数据,但是它们通常依赖于笨拙的技术,例如“

最近把阿里一个千万级 PV 的数据产品全部由 jQuery
的 $.ajax 迁移到 Fetch,上线一个多月以来运行非常稳定。结果证明,对于
IE8+ 以上浏览器,在生产环境使用 Fetch 是可行的。

注入或第三方插件。MicrosoftXMLHttpRequest为Outlook电子邮件客户端开发了一种基于浏览器的替代产品。

由于 Fetch API 是基于 Promise 设计,有必要先学习一下
Promise,推荐阅读 MDN Promise
教程。旧浏览器不支持
Promise,需要使用
polyfill es6-promise 。

XMLHttpRequest直到2006年才成为Web标准,但大多数浏览器都实现了该标准。它在Gmail和Google
Maps中的采用导致Jesse James
Garrett在2005年发表的文章AJAX:一种新的Web应用程序方法。新术语明确了开发人员的重点。

本文不是 Fetch API 科普贴,其实是讲异步处理和 Promise 的。Fetch API
很简单,看文档很快就学会了。推荐 MDN Fetch
教程 和
万能的WHATWG Fetch 规范

AJAX to Ajax

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 来做最终优化:

注:async/await 是非常新的 API,属于 ES7,目前尚在 Stage 1(提议)
阶段,这是它的完整规范。使用Babel 开启 runtime 模式后可以把
async/await 无痛编译成 ES5
代码。也可以直接使用 regenerator 来编译到
ES5。

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

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

Promise,generator/yield,await/async 都是现在和未来 JS
解决异步的标准做法,可以完美搭配使用。这也是使用标准 Promise
一大好处。最近也把项目中使用第三方 Promise 库的代码全部转成标准
Promise,为以后全面使用 async/await 做准备。

另外,Fetch 也很适合做现在流行的同构应用,有人基于 Fetch 的语法,在 Node
端基于 http
库实现了node-fetch,又有人封装了用于同构应用的 isomorphic-fetch。

注:同构(isomorphic/universal)就是使前后端运行同一套代码的意思,后端一般是指
NodeJS 环境。

总结一下,Fetch 优点主要有:

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

AJAX是异步JavaScript和XML的助记符。绝对是“异步的”,但是:

Fetch 启用方法

下面是重点↓↓↓

先看一下 Fetch 原生支持率:
新澳门萄京娱乐场官网 2

原生支持率并不高,幸运的是,引入下面这些 polyfill 后可以完美支持 IE8+ :

  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
实现。这也是github/fetch 的做法,但是有些浏览器(Chrome
45)原生支持
Fetch,但响应中有中文时会乱码,老外又不太关心这种问题,所以我自己才封装了 fetch-detector 和 fetch-ie8 只在浏览器稳定支持
Fetch 情况下才使用原生
Fetch。这些库现在 每天有几千万个请求都在使用,绝对靠谱 !

终于,引用了这一堆 polyfill 后,可以愉快地使用 Fetch
了。但要小心,下面有坑:

可以使用JavaScript,尽管可以选择VBScript和Flash

Fetch 常见坑

  • Fetch 请求默认是不带 cookie
    的,需要设置 fetch(url, {credentials: 'include'})
  • 服务器返回 400,500 错误码时并不会
    reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。

竟然没有提到 IE,这实在太不科学了,现在来详细说下 IE

有效负载不必是XML,尽管在当时很流行。可以使用任何数据格式,今天,通常首选JSON。

IE 使用策略

所有版本的 IE 均不支持原生 Fetch,fetch-ie8 会自动使用 XHR 做
polyfill。但在跨域时有个问题需要处理。

IE8, 9 的 XHR 不支持 CORS
跨域,虽然提供 XDomainRequest,但这个东西就是玩具,不支持传
Cookie!如果接口需要权限验证,还是乖乖地使用 jsonp
吧,推荐使用 fetch-jsonp。如果有问题直接提
issue,我会第一时间解决。

现在,我们将“
Ajax”用作任何客户端过程的通用术语,该过程从服务器获取数据并动态更新DOM,而无需刷新整个页面。Ajax是大多数Web应用程序和单页应用程序的核心技术。

Fetch 和标准 Promise 的不足

由于 Fetch 是典型的异步场景,所以大部分遇到的问题不是 Fetch 的,其实是
Promise 的。ES6 的 Promise
是基于 Promises/A+ 标准,为了保持 简单简洁 ,只提供极简的几个
API。如果你用过一些牛 X 的异步库,如 jQuery(不要笑) 、Q.js 或者
RSVP.js,可能会感觉 Promise 功能太少了。

极端XMLHttpRequest

没有 Deferred

Deferred 可以在创建
Promise 时可以减少一层嵌套,还有就是跨方法使用时很方便。
ECMAScript 11 年就有过 Deferred
提案,但后来没被接受。其实用
Promise 不到十行代码就能实现
Deferred:es6-deferred。现在有了
async/await,generator/yield 后,deferred 就没有使用价值了。

以下JavaScript代码示出了用于一个基本HTTP
GET请求http://domain/service使用XMLHttpRequest

没有获取状态方法:isRejected,isResolved

标准 Promise 没有提供获取当前状态 rejected 或者 resolved
的方法。只允许外部传入成功或失败后的回调。我认为这其实是优点,这是一种声明式的接口,更简单。

letxhr=newXMLHttpRequest();xhr.open('GET','http://domain/service');//requeststatechangeeventxhr.onreadystatechange=function(){//requestcompleted?ifreturn;if{//requestsuccessful-showresponseconsole.log;}else{//requesterrorconsole.log('HTTPerror',xhr.status,xhr.statusText);}};//startrequestxhr.send();

缺少其它一些方法:always,progress,finally

always 可以通过在 then 和 catch 里重复调用方法实现。finally
也类似。progress 这种进度通知的功能还没有用过,暂不知道如何替代。

XMLHttpRequest对象具有许多其他选项,事件和响应属性。例如,可以设置和检测以毫秒为单位的超时:

不能中断,没有 abort、terminate、onTimeout 或 cancel 方法

Fetch 和 Promise 一样,一旦发起,不能中断,也不会超时,只能等待被
resolve 或 reject。幸运的是,whatwg
目前正在尝试解决这个问题 whatwg/fetch#27

//settimeoutxhr.timeout=3000;//3secondsxhr.ontimeout=()=>console.log('timeout',xhr.responseURL);

资料

  • WHATWG Fetch 规范
  • Fetch API
    简介
  • 教你驯服
    async
  • 阮一峰介绍
    async

一个progress事件可以报告长时间运行的文件上传:

最后

Fetch 替换 XHR 只是时间问题,现在看到国外很多新的库都默认使用了 Fetch。

最后再做一个大胆预测:由于 async/await 这类新异步语法的出现,第三方的
Promise 类库会逐渐被标准 Promise 替代,使用 polyfill
是现在比较明智的做法。

如果你觉得本文对你有帮助,请点击右上方的 Star 鼓励一下,或者点击 Watch
订阅

//uploadprogressxhr.upload.onprogress=p=>{console.log(Math.round*100)+'%');}

选项的数量可能令人困惑,并且早期实现XMLHttpRequest存在一些跨浏览器不一致的情况。因此,大多数库和框架都提供Ajax包装函数来处理复杂性,例如jQuery.ajax()方法:

//jQueryAjax$.ajax('http://domain/service').done(data=>console.log.fail=>console.log('error:',status));

Fast Forward to Fetch

该提取API是一个现代化的替代品XMLHttpRequest。通用的Headers,Request和Response接口提供了一致性,而Promises允许更容易的链接和异步/等待而没有回调。上面的XHR示例可以转换为更简单的基于Fetch的代码,甚至可以解析返回的JSON:

fetch('http://domain/service',{method:'GET'}).then(response=>response.json.then(json=>console.log.catch(error=>console.error('error:',error));

Fetch干净,优雅,易于理解,并且在PWA Service
Worker中大量使用。为什么不使用它代替古老的XMLHttpRequest?

不幸的是,Web开发从未如此明确。Fetch尚未完全替代Ajax技术…

浏览器支持

Fetch API得到了很好的支持,但是在所有版本的Internet
Explorer中都将失败。使用2017年之前版本的Chrome,Firefox和Safari的人可能也会遇到问题。这些用户可能只占您用户群的一小部分……或者可能是主要客户。开始编码之前,请务必检查!

此外,与成熟的XHR对象相比,Fetch
API较新,并且可以接收更多正在进行的更改。这些更新不太可能破坏代码,但是希望在未来几年中进行一些维护工作。

默认无Cookie

与不同XMLHttpRequest,并非所有Fetch的实现都会发送Cookie,因此您的应用程序的身份验证可能会失败。可以通过更改第二个参数中传递的启动选项来解决此问题,例如

fetch('http://domain/service',{method:'GET',credentials:'same-origin'})

错误不会被拒绝

令人惊讶的是,HTTP错误(例如404 Page Not Found或)500 Internal Server Error不会导致Fetch
Promise拒绝;将.catch()永远不会运行。通常,它将response.ok状态设置为false进行解析。

仅当请求无法完成时才发生拒绝。这会使错误捕获的实现更加复杂。

不支持超时

提取不支持超时,只要浏览器选择,请求将继续。需要进一步的代码将Fetch包装在另一个Promise中,例如

//fetchwithatimeoutfunctionfetchTimeout(url,init,timeout=3000){returnnewPromise=>{fetch.then.catch;setTimeout;}}

…或也许Promise.race()首先获取或超时完成时使用哪个解析,例如:

Promise.race([fetch('http://url',{method:'GET'}),newPromise(resolve=>setTimeout.then(response=>console.log

Aborting a Fetch

轻松终止XHR请求,xhr.abort()并在必要时使用xhr.onabort功能检测此类事件。

几年来无法中止Fetch,但是现在实现AbortController
API的浏览器支持它。这将触发一个信号,该信号可以传递给Fetch启动对象:

constcontroller=newAbortController();fetch('http://domain/service',{method:'GET'signal:controller.signal}).then(response=>response.json.then(json=>console.log.catch(error=>console.error('Error:',error));

可以通过调用中止获取controller.abort();。Promise拒绝,因此.catch()调用该函数。

No Progress

在撰写本文时,Fetch不支持进度事件。因此,不可能报告文件上传或类似的大型表单提交的状态。

XMLHttpRequest与Fetch API?

最终,选择是您自己的……除非您的应用程序具有要求上载进度条的IE客户端。

对于更简单的Ajax调用,它XMLHttpRequest是较低级别的,更复杂的,并且您将需要包装函数。不幸的是,一旦您开始考虑超时,调用中止和错误陷阱的复杂性,Fetch也会如此。

您可以选择将Fetch polyfill与Promise
polyfill结合使用,以便可以在IE中编写Fetch代码。但是,XHR被用作备用。并非每个选项都能按预期工作,例如,无论设置如何,都会发送Cookie。

获取是未来。但是,该API是相对较新的,它不提供所有XHR功能,并且某些选项比较繁琐。在接下来的几年中请谨慎使用。

总结

总而言之,XMLHttpRequest和Fetch
API,对于我们Web前端工程师,都必须掌握,不同场景,不同项目,好自用之即可。

发表评论

电子邮件地址不会被公开。 必填项已用*标注