• fetch ios低版本兼容 cannot clone a disturbed response


    报错信息

    ios 11以下 cannot clone a disturbed response

    github.com/github/fetc…

    问题发生场景

    • 使用了一个或者多个三方库
    • 三方库或者自己的业务代码重写了fetch
    • ios11以下

    核心原因 ios低版本兼容问题,fetch的原始响应clone一次解析后,不能再次clone(浏览器报错信息:cannot clone a disturbed response)

    我们使用fetch的响应的时候,如果直接通过方法解析2次,第二次就会报错 body stream already read

    fetch("/").then(res=>{
        res.text().then((r)=>{console.log(r)})
        res.text().then((r)=>{console.log(r)})
    });

    所以一般会使用clone,如下的写法。这样的写法有兼容问题,ios11以下会报错: cannot clone a disturbed response

    fetch("/").then(res=>{
        res.clone().text().then((r)=>{console.log(r)})
        res.clone().text().then((r)=>{console.log(r)})
    }); 

    这个时候有同学会问了,谁会这样写啊,一般解析一次就够了,干嘛解析两次。如果使用了三方库就会出现这种问题,一般三方库会重写fetch的。三方库可能是请求库(umi-request),也可能是调试库(eduda、vconsole),等等。三方库,会重写fetch,为了拦截API写点自己需要的代码,大概是下面这样的:

    // 三方库重写fetch代码
    const originFetch = fetch;
    fetch = function(){
        // do some
        return originFetch
              .apply(this, arguments)
              .then((res) => {
                  // do some
                  res.clone().text().then((data) => {
                      // do some
                  })
                  return res
              })
    }
    // 业务代码
    fetch('/').then(res=>{
        res.clone().text()
    })

    如上代码,返回的 res 已经被三方库 clone 过了,如果再次 clone 便会出现ios11以下的兼容报错。所以我们的业务代码会直接报错,拿不到任何响应。

    三方库分析

    umi/request

    umi/request,发现了这个问题,并且做了代码的处理. ( github.com/umijs/umi-r… )

    image.png

    github.com/umijs/umi-r…

    image.png

    从目前的代码看起来,这个解决方案只是解决了它内部使用的问题,而且它返回的数据并不是fetch的原始响应,而是它解析后的接口结果。

    现在假如我们在umi/request之后,再实例化使用vconsle,或者eruda,这两个库会重写fetch。两个库同时存在的时候,res.clone 就会触发开始说的ios低版本问题。

     

    vconsole

    下面这段是vconsole的fetch代码

    image.png

     

    eruda

    github.com/liriliri/ch…

    image.png

    github.com/liriliri/ch…

    image.png

    几乎大多的库都如上面,fetch返回的原始响应在库内部被clone过后,原始响应再流转下去。流转下去以后其他的三方库或者业务代码,执行clone便会触发ios11以下的兼容问题。就像是执行了下面的代码一样。

    fetch("/").then(res=>{
        // 第一次clone
        res.clone().text().then((r)=>{console.log(r)})
        return res
    }).then(res=>{
        // 第二次clone
        res.clone().text().then((r)=>{console.log(r)})
    });

     

    解决方案

    如果业务代码使用原生fetch只会解析一次fetch响应,可以忽略因为不会触发两次clone。 作为三方库的开发者,应该知道有这样的兼容问题,下面的写法ios11以下也不会有问题。

    fetch("/").then(res=>{
        // 第一次clone
        const C1 = res.clone();
        const C2 = res.clone();
        C1.clone().text().then((r)=>{console.log(r)})
        C2.clone().text().then((r)=>{console.log(r)})
    })
    // else
    fetch("/").then(res=>{
        // 第一次clone
        const C1 = res.clone();
        C1.clone().text().then((r)=>{console.log(r)})
        C1.clone().text().then((r)=>{console.log(r)})
    })
    // else...

    理一下关系

    会出兼容性问题的写法图示

    image.png

    解决方案图示

    image.png

    我们多clone一级,就能解决这个问题,这和clone本身的意义实际有出处。ios11以下的这个兼容问题,应该是以前的bug,这个bug在ios11以后才修复,总之现在这样就能解决问题。

    解决方案已经明确了,三方库推荐如下方式修改clone方法。

    const originFetch = fetch;
    fetch = function(){
        // do some
        return originFetch
              .apply(this, arguments)
              .then((res) => {
                  const copyClone = res.clone();
                  // do some
                  copyClone.clone().text().then((data) => {
                      // do some
                  })
                  return copyClone.clone()
              })
    }

    如果同时引入多个三方库,其中一个已经按照下面写法解决了兼容性问题,一个还没有解决,可以让解决了兼容的库先执行,也能保证运行正常。

    同时还发现,ios11以下,fetch finally方法undefined,不能使用finally方法

    ps: 水印就不去了,先在掘金编辑的,拷贝过来多平台发布,本文章为原创文。

    有没有人打赏?没有的话,那我晚点再来问问。
    关注大诗人公众号,第一时间获取最新文章。
    如果你有购买钢琴的打算,可以从这里了解到在售信息,价格实惠品质保障。

    ---转发请标明,并添加原文链接---
  • 相关阅读:
    20.Docker Swarm集群
    Phalcon下命令行应用(command line applications)
    Mysql 命令备忘
    PHPExcel+phalcon+yii批量导入
    Yii 之分页 + bootstrap
    PHPMailer+phalcon中使用
    Phalcon之分页
    虚拟机中ip和host设置问题
    php RSS订阅功能
    ubuntu/linux下设置永久路由
  • 原文地址:https://www.cnblogs.com/1wen/p/15248350.html
Copyright © 2020-2023  润新知