任逍遥

 找回密码
 立即注册
查看: 30633|回复: 100

web前端杂谈:传统的 Ajax 已死,Fetch得永生!

    [复制链接]
  • TA的每日心情
    郁闷
    2022-12-4 03:02
  • 签到天数: 495 天

    连续签到: 2 天

    [LV.9]妙领天机

    727

    主题

    3613

    帖子

    5085

    积分

    VIP会员

    Rank: 16Rank: 16Rank: 16Rank: 16

    积分
    5085
    发表于 2018-7-23 20:38:49 | 显示全部楼层 |阅读模式

    马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x


      原谅我做一次标题党,Ajax 不会死,传统 Ajax 指的是 XMLHttpRequest(XHR),未来现在已被 Fetch 替代。
      最近把阿里一个千万级 PV 的数据产品全部由 jQuery 的  $.ajax 迁移到 Fetch,上线一个多月以来运行非常稳定。结果证明,对于 IE8+ 以上浏览器,在生产环境使用 Fetch 是可行的。
      由于 Fetch API 是基于 Promise 设计,有必要先学习一下 Promise,推荐阅读 MDN Promise 教程。旧浏览器不支持 Promise,需要使用 polyfill es6-promise 。
      本文不是 Fetch API 科普贴,其实是讲异步处理和 Promise 的。Fetch API 很简单,看文档很快就学会了。推荐 MDN Fetch 教程 和 万能的WHATWG 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 来做最终优化:
      注: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 优点主要有:
      语法简洁,更加语义化
      基于标准 Promise 实现,支持 async/await
      同构方便,使用 isomorphic-fetch
      Fetch 启用方法
      下面是重点↓↓↓
      先看一下 Fetch 原生支持率:

      原生支持率并不高,幸运的是,引入下面这些 polyfill 后可以完美支持 IE8+ :
      由于 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
      引入 Promise 的 polyfill: es6-promise
      引入 fetch 探测库:fetch-detector
      引入 fetch 的 polyfill: fetch-ie8
      可选:如果你还使用了 jsonp,引入 fetch-jsonp
      可选:开启 Babel 的 runtime 模式,现在就使用 async/await
      Fetch polyfill 的基本原理是探测是否存在 window.fetch 方法,如果没有则用 XHR 实现。这也是 github/fetch 的做法,但是有些浏览器(Chrome 45)原生支持 Fetch,但响应中有中文时会乱码,老外又不太关心这种问题,所以我自己才封装了 fetch-detector 和 fetch-ie8 只在浏览器稳定支持 Fetch 情况下才使用原生 Fetch。这些库现在 每天有几千万个请求都在使用,绝对靠谱 !
      终于,引用了这一堆 polyfill 后,可以愉快地使用 Fetch 了。但要小心,下面有坑:
      Fetch 常见坑
      Fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: 'include'})
      服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
      竟然没有提到 IE,这实在太不科学了,现在来详细说下 IE
      IE 使用策略
      所有版本的 IE 均不支持原生 Fetch,fetch-ie8 会自动使用 XHR 做 polyfill。但在跨域时有个问题需要处理。
      IE8, 9 的 XHR 不支持 CORS 跨域,虽然提供 XDomainRequest,但这个东西就是玩具,不支持传 Cookie!如果接口需要权限验证,还是乖乖地使用 jsonp 吧,推荐使用 fetch-jsonp。如果有问题直接提 issue,我会第一时间解决。
      Fetch 和标准 Promise 的不足
      由于 Fetch 是典型的异步场景,所以大部分遇到的问题不是 Fetch 的,其实是 Promise 的。ES6 的 Promise 是基于 Promises/A+ 标准,为了保持 简单简洁 ,只提供极简的几个 API。如果你用过一些牛 X 的异步库,如 jQuery(不要笑) 、Q.js 或者 RSVP.js,可能会感觉 Promise 功能太少了。
      没有 Deferred
      Deferred 可以在创建 Promise 时可以减少一层嵌套,还有就是跨方法使用时很方便。
      ECMAScript 11 年就有过 Deferred 提案,但后来没被接受。其实用 Promise 不到十行代码就能实现 Deferred:es6-deferred。现在有了 async/await,generator/yield 后,deferred 就没有使用价值了。
      没有获取状态方法:isRejected,isResolved
      标准 Promise 没有提供获取当前状态 rejected 或者 resolved 的方法。只允许外部传入成功或失败后的回调。我认为这其实是优点,这是一种声明式的接口,更简单。
      缺少其它一些方法:always,progress,finally
      always 可以通过在 then 和 catch 里重复调用方法实现。finally 也类似。progress 这种进度通知的功能还没有用过,暂不知道如何替代。
      不能中断,没有 abort、terminate、onTimeout 或 cancel 方法
      Fetch 和 Promise 一样,一旦发起,不能中断,也不会超时,只能等待被 resolve 或 reject。幸运的是,whatwg 目前正在尝试解决这个问题 whatwg/fetch#27
      资料
      WHATWG Fetch 规范
      Fetch API 简介
      教你驯服 async
      阮一峰介绍 async
      最后
      Fetch 替换 XHR 只是时间问题,现在看到国外很多新的库都默认使用了 Fetch。
      最后再做一个大胆预测:由于 async/await 这类新异步语法的出现,第三方的 Promise 类库会逐渐被标准 Promise 替代,使用 polyfill 是现在比较明智的做法。
    楼主热帖
  • TA的每日心情

    2019-8-29 09:07
  • 签到天数: 467 天

    连续签到: 1 天

    [LV.9]妙领天机

    5

    主题

    2866

    帖子

    2865

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2865
    发表于 2018-7-23 20:54:46 | 显示全部楼层
    支持支持再支持
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2019-8-30 13:35
  • 签到天数: 475 天

    连续签到: 2 天

    [LV.9]妙领天机

    1

    主题

    2916

    帖子

    2918

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2918
    发表于 2018-7-24 20:45:01 | 显示全部楼层
    打酱油的人拉,回复下赚取积分
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-8-30 14:37
  • 签到天数: 476 天

    连续签到: 4 天

    [LV.9]妙领天机

    5

    主题

    2974

    帖子

    2978

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2978
    发表于 2018-7-26 03:45:40 | 显示全部楼层
    支持楼主,用户楼主,楼主英明呀!!!
    回复

    使用道具 举报

  • TA的每日心情
    难过
    2019-8-30 03:26
  • 签到天数: 461 天

    连续签到: 2 天

    [LV.9]妙领天机

    6

    主题

    2872

    帖子

    2875

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2875
    发表于 2018-7-26 04:04:01 | 显示全部楼层
    路过 帮顶 嘿嘿
    回复

    使用道具 举报

  • TA的每日心情
    郁闷
    2022-12-4 03:02
  • 签到天数: 495 天

    连续签到: 2 天

    [LV.9]妙领天机

    727

    主题

    3613

    帖子

    5085

    积分

    VIP会员

    Rank: 16Rank: 16Rank: 16Rank: 16

    积分
    5085
     楼主| 发表于 2018-7-26 09:44:23 | 显示全部楼层
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2019-8-29 16:12
  • 签到天数: 475 天

    连续签到: 3 天

    [LV.9]妙领天机

    0

    主题

    2880

    帖子

    2873

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2873
    发表于 2018-7-27 16:11:11 | 显示全部楼层
    支持楼主,用户楼主,楼主英明呀!!!
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-8-28 23:17
  • 签到天数: 463 天

    连续签到: 3 天

    [LV.9]妙领天机

    5

    主题

    2840

    帖子

    2844

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2844
    发表于 2018-7-29 10:45:17 | 显示全部楼层
    这么强,支持楼主,佩服
    回复

    使用道具 举报

  • TA的每日心情
    难过
    2019-8-30 02:46
  • 签到天数: 475 天

    连续签到: 8 天

    [LV.9]妙领天机

    3

    主题

    2956

    帖子

    2959

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2959
    发表于 2018-7-30 20:29:07 | 显示全部楼层
    打酱油的人拉,回复下赚取积分
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-8-29 09:37
  • 签到天数: 453 天

    连续签到: 1 天

    [LV.9]妙领天机

    7

    主题

    2881

    帖子

    2887

    积分

    声名显赫

    Rank: 7Rank: 7Rank: 7

    积分
    2887
    发表于 2018-8-1 19:11:00 | 显示全部楼层
    我只是路过,不发表意见
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|任逍遥

    GMT+8, 2024-5-3 17:24 , Processed in 0.086887 second(s), 46 queries .

    Powered by 任逍遥 X3.4

    Copyright © 2001-2023, Rxiaoyao Cloud.

    快速回复 返回顶部 返回列表