ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-09-09 22:33:16  阅读:251  来源: 互联网

标签:res log text clone ios 低版本 console fetch


报错信息

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的。三方库可能是请求库(axois、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: 水印就不去了,先在掘金编辑的,拷贝过来多平台发布,本文章为原创文。

标签:res,log,text,clone,ios,低版本,console,fetch
来源: https://www.cnblogs.com/1wen/p/15248350.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有