目录
从 Ajax 到 Ajax
- XMLHttpRequest
- Fetch
浏览器支持
默认无 Cookie
错误不会被拒绝
不支持超时
中止 Fetch
没有 Progress
XMLHttpRequest vs Fetch?
2019 年是 Ajax 诞生的 20 周年. 可以说, XMLHttpRequest 的第一次实现是在 1999 年作为 IE5.0 ActiveX 组件发布.
在此之前, 曾经有一些方法可以在不刷新页面的情况下从服务器获取数据, 但是他们通常依赖笨拙的技术, 例如 < script > 注入或第三方插件. 微软开发了 XMLHttpRequest 初始版本, 用于替代 Outlook 基于浏览器的电子邮件客户端.
XMLHttpRequest 直到 2006 年才成为 web 标准, 但在此之前已在大多数浏览器中被实现. 由于它在 Gmail 和 Google Maps 中的采用, Jesse James Garrett 在 2005 年发表了一篇文章: Ajax: A New Approach to Web Applications. 这个新术语吸引了开发人员的关注.
从 Ajax 到 Ajax
Ajax 是 Asynchronous JavaScript and xml 的简称."Asynchronous" 一词很明显, 但是:
虽然 VBScript 和 Flash 也可以实现, 但是 JavaScript 更合适
有效负载不必是 xml, 尽管在当时很流行. 在今天, 可以使用任何的数据格式, 通常 JSON 是首选.
现在, 我们将 "Ajax" 用作客户端从服务器获取数据并动态更新 DOM, 而无需刷新整个页面的通用术语. Ajax 是大多数 Web 应用程序和单页应用程序 (SPA) 的核心技术.
XMLHttpRequest
以下 JavaScript 代码展示了如何使用 XMLHttpRequest(通常简称为 XHR)向 http://domain/service 发出的 HTTP GET 请求.
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://domain/service');
- // request state change event
- xhr.onreadystatechange = function() {
- // request completed?
- if (xhr.readyState !== 4) return;
- if (xhr.status === 200) {
- // request successful - show response
- console.log(xhr.responseText);
- } else {
- // request error
- console.log('HTTP error', xhr.status, xhr.statusText);
- }
- };
- // start request
- xhr.send();
对象有许多属性, 方法和事件. 例如, 可以设置和监测以毫秒为单位的超时:
- // set timeout
- xhr.timeout = 3000; // 3 seconds
- xhr.ontimeout = () => console.log('timeout', xhr.responseURL);
并且 progress 事件可以报告长时间运行的文件上传:
- // upload progress
- xhr.upload.onprogress = p => {
- console.log( Math.round((p.loaded / p.total) * 100) + '%') ;
- }
属性的数量可能令人困惑, 而且 XMLHttpRequest 的早期实现在跨浏览器之间也不一致. 因此, 很多库和框架都提供了 Ajax 的封装函数, 例如 jQuery.Ajax()方法:
- // jQuery Ajax
- $.Ajax('http://domain/service')
- .done(data => console.log(data))
- .fail((xhr, status) => console.log('error:', status));
- Fetch
Fetch API 是 XMLHttpRequest 的现代替代方案. 通用的, 和接口提供了一致性, 同时 Promises 允许更简单的的链式调用和不需要回调的 async/await.
- fetch(
- 'http://domain/service',
- { method: 'GET' }
- )
- .then( response => response.JSON() )
- .then( JSON => console.log(JSON) )
- .catch( error => console.error('error:', error) );
Fetch 简洁, 优雅, 易于理解, 并且在 PWA Service Worker 中大量使用. 为什么不用它替代古老的 XMLHttpRequest 呢?
不幸的是, Web 开发从未如此明确. Fetch 还不是用于 Ajax 的完美替代品...
浏览器支持
Fetch API 在大部分浏览器中得到了很好的支持 https://caniuse.com/#search=fetch , 但是不支持所有版本的 IE. 使用 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 标记为 reject;.catch()也不会被执行. 想要精确的判断 fetch 是否成功, 需要包含 promise resolved 的情况, 此时再判断 response.ok 是不是为 true. 如下:
- fetch(
- 'http://domain/service',
- { method: 'GET' }
- )
- .then( response => {
- if(response.ok) {
- return response.JSON();
- }
- throw new Error('Network response was not ok.');
- })
- .then( JSON => console.log(JSON) )
- .catch( error => console.error('error:', error) );
仅当请求无法完成时才触发 reject, 例如网络故障或请求被阻止. 这会使错误捕获更加复杂.
不支持超时
Fetch 不支持超时, 只要浏览器允许, 请求将继续. 解决方法是可以将 Fetch 包装在一个 Promise 中, 例如:
- // fetch with a timeout
- function fetchTimeout(url, init, timeout = 3000) {
- return new Promise((resolve, reject) => {
- fetch(url, init)
- .then(resolve)
- .catch(reject);
- setTimeout(reject, timeout);
- }
- }
或使用 Promise.race()决定 fetch 或 timeout 何时首先完成, 例如:
- Promise.race([
- fetch('http://url', { method: 'GET' }),
- new Promise(resolve => setTimeout(resolve, 3000))
- ])
- .then(response => console.log(response))
中止 Fetch
通过 xhr.abort()很容易结束一个 XHR 请求, 另外也可以通过 xhr.onabort 函数监测事件解决.
之前一直无法中止一个 Fetch 请求, 但是现在实现了 AbortController API 的浏览器可以支持它. 这将触发一个信号, 该信号可以传递给 Fetch 启动对象:
- const controller = new AbortController();
- fetch(
- 'http://domain/service',
- {
- method: 'GET'
- signal: controller.signal
- })
- .then( response => response.JSON() )
- .then( JSON => console.log(JSON) )
- .catch( error => console.error('Error:', error) );
Fetch 可以通过调用 controller.abort()来中止. Promise 被标记 reject 后, 会调用. catch()函数.
没有 Progress
在撰写本文时, Fetch 仍不支持进度事件. 因此, 不可能显示文件上传或大型表单提交的进度状态.
XMLHttpRequest vs Fetch?
最后, 选择还是看你自己...... 除非你的应用是要求有上传进度条的 IE 客户端. 你也可以选择将 Fetch polyfill https://github.github.io/fetch/ 与 Promise polyfill https://www.npmjs.com/package/promise-polyfill 结合使用, 以便在 IE 中执行 Fetch 代码.
对于更简单的 Ajax 调用, XMLHttpRequest 是低级别的, 更复杂的, 并且你需要封装函数. 不幸的是, 一旦你开始考虑超时, 中止调用和错误捕获的复杂性, Fetch 也会如此.
Fetch 的未来可期. 但是, 该 API 是相对较新, 它不提供所有 XHR 的功能, 并且某些参数设置比较繁琐. 因此在以后的使用过程中, 请注意上述问题.
来源: https://www.cnblogs.com/hanksyao/p/12089105.html