前端网络基础 - fetch

article/2025/7/9 15:42:26

目录

XMLHttpRequest缺点

fetch的优点

fetch的请求和响应设计

Request

Response

fetch函数的用法

fetch取消请求

fetch的异常结果

fetch和axios的区别


XMLHttpRequest缺点

浏览器提供了原生的AJAX实现类XMLHttpRequest,基于该类实例,我们可以实现在网页上发送AJAX请求到服务端。

但是XMLHttpRequest的设计并不完美,主要体现在以下几个方面:

  • HTTP请求,响应都被耦合在XMLHttpRequest实例上,结构不够简单明了
  • 采用事件回调的方式获取HTTP响应,可能会产生回调地狱
  • 如果HTTP响应数据过大,则会占用大量内存
  • 最后一点就是,XMLHttpRequest实现AJAX的步骤太零碎了

我们分别举例说明下

const xhr = new XMLHttpRequest()xhr.open('post', 'http://localhost:3000/test')xhr.setRequestHeader('Content-Type', 'application/json')xhr.responseType = 'json'xhr.send(JSON.stringify({name: 'qfc',age: 18
}))xhr.onreadystatechange = function(){if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status < 300){console.log(xhr.status)console.log(xhr.statusText)console.log(xhr.getAllResponseHeaders())console.log(xhr.response)}}
}

通过以上代码,我们发现,HTTP请求URL,请求METHOD,请求HEAD,请求BODY,全部依赖于xhr的方法进行设置,HTTP响应状态码,状态描述,响应HEAD,响应BODY,也全部依赖于xhr来获取,这其实不符合高内聚,低耦合要求,我们期望将HTTP请求所有的信息封装在一起,将HTTP响应的所有信息封装在一起。

// 下面案例是 先查询订单123的信息,再根据订单123信息中productId去查询商品信息
const xhr = new XMLHttpRequest()
xhr.open('get', 'http://localhost:3000/order/123')
xhr.responseType = 'json'
xhr.send()xhr.onreadystatechange = function(){if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status < 300){const xhr2 = new XMLHttpRequest()xhr2.open('get', `http://localhost:3000/product/${xhr.response.productId}`)xhr2.responseType = 'json'xhr2.send()xhr2.onreadystatechange = function(){if(xhr2.readyState === 4) {if(xhr2.status >= 200 && xhr2.status < 300) {console.log(xhr2.response)}}}}}
}

上面代码就是典型的回调地狱式的异步串行案例。我们期望基于Promise#then的链式串行,或者更进一步的async await异步任务同步化执行。

XMLHttpRequest实例自身有一个属性readyState,该属性有如下几个值

  • 0:xhr实例创建
  • 1:xhr.open调用
  • 2:xhr.send调用
  • 3:xhr收到部分HTTP响应
  • 4:xhr收到全部HTTP响应

而当xhr.readyState属性值改变时,就会触发xhr.onreadystatechange事件,我们通过监听该事件,就可以知道HTTP响应是否已被收到,收到的HTTP响应会被挂载到xhr实例上,我们可以通过xhr.status,xhr.statusText,xhr.getAllResponseHeaders(),xhr.response来获得HTTP响应信息。

但是xhr并没有实现流式获取HTTP响应,即无法分块获取HTTP响应,当HTTP响应过大时,需要占用对应大小的内存,将HTTP响应全部缓存再内存中,这是内存不友好的。

fetch的优点

fetch和XMLHttpRequest一样,也是浏览器原生的,用于发送AJAX请求。

 但是fetch是在XMLHttpRequest之后诞生的,它旨在解决XMLHttpRequest的不足,所以XMLHttpRequest的缺点就是它的优点,具体优点如下

  • 语法简单,结构清晰明了
  • 支持Promise获取异步的HTTP响应
  • HTTP响应支持流式获取,内存友好

fetch被设计为函数,通过fetch函数调用即可发起AJAX,而不需要像XMLHttpRequest那样创建实例,然后基于xhr实例发起AJAX。

fetch('http://localhost:3000/test') // fetch函数调用即发起AJAX

fetch函数返回一个Promise对象,而Promise对象的结果值就是HTTP响应

fetch('http://localhost:3000/test').then(response => { // fetch函数返回值是一个Promise类型对象console.log(response) // 该Promise对象的结果值response就是HTTP响应
})

fetch函数返回的Promise对象的结果值HTTP响应是流式获取,即使HTTP响应数据很大,也不会占用过多的内存。

fetch的请求和响应设计

fetch将HTTP请求信息封装在一个Request类中,将HTTP响应封装在一个Response类中,Request和Response类都是浏览器原生的,我们可以直接使用。

例如:fetch函数返回的Promise对象的结果值response就是Response类的实例

例如:我们可以创建一个Request对象,作为fetch函数入参

下面将详细介绍Request,Response

Request

Request() - Web API 接口参考 | MDN (mozilla.org)

var myRequest = new Request(input[, init]);

Request构造函数语法如上

input是必选参数,一般传入URL字符串,如'http://localhost:3000/test',或者传入一个Request对象

init是可以选参数,需要传入一个对象,对象可以包含如下属性

methodHTTP请求方法
headersHTTP请求头
bodyHTTP请求体。可以是Blob, BufferSource (en-US), FormData, URLSearchParams, USVString,或ReadableStream对象。
mode

请求的模式

  • cors:默认值,允许跨域请求。
  • same-origin:只允许同源请求。
  • no-cors:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求。
credentials

是否发送 Cookie

  • same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送。
  • include:不管同源请求,还是跨域请求,一律发送 Cookie。
  • omit:一律不发送。
cache

HTTP缓存设置

  • default:默认值,先在缓存里面寻找匹配的请求。
  • no-store:直接请求远程服务器,并且不更新缓存。
  • reload:直接请求远程服务器,并且更新缓存。
  • no-cache:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存。
  • force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器。
  • only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误。
redirect对重定向处理的模式: followerror, or manual。在Chrome中,Chrome 47 之前的版本默认值为 manual ,自Chrome 47起,默认值为follow。
referrer 用于设定fetch()请求的referer标头
referrerPolicy

用于设定Referer标头的规则

  • no-referrer-when-downgrade:默认值,总是发送Referer标头,除非从 HTTPS 页面请求 HTTP 资源时不发送。
  • no-referrer:不发送Referer标头。
  • originReferer标头只包含域名,不包含完整的路径。
  • origin-when-cross-origin:同源请求Referer标头包含完整的路径,跨域请求只包含域名。
  • same-origin:跨域请求不发送Referer,同源请求发送。
  • strict-originReferer标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer标头。
  • strict-origin-when-cross-origin:同源请求时Referer标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头。
  • unsafe-url:不管什么情况,总是发送Referer标头。
intergrity 指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值。比如,下载文件时,检查文件的 SHA-256 哈希值是否相符,确保没有被篡改
signal指定一个 AbortSignal 实例,用于取消fetch()请求
keepalive用于页面卸载时,告诉浏览器在后台保持连接,继续发送数据。
const req = new Request('http://localhost:3000/test', {method: 'post',headers: {'Content-Type': 'application/json'},body: JSON.stringify({name: 'qfc',age: 18})
})fetch(req).then(res => {console.log(res)
})

其中需要注意的是Request对象的body属性,该属性值支持

  • 查询参数字符串,如'name=qfc&age=18'
  • 文本字符串,如'{"name":"qfc", "age": 18}'
  • FormData对象
  • Blob对象
  • ReadableStream对象
  • BufferSource对象

居然不支持普通JS对象,这让我过于意外,在如今application/json数据格式的天下,居然不默认支持将JS对象自动转为JSON字符串...

body传入普通JS对象,服务器直接报错了,因为服务器收到的是一个JS对象(二进制类型),而不是一个JSON字符串(文本类型),所以无法进行JSON解析,所以报错了。

Response

Response - Web API 接口参考 | MDN (mozilla.org)

通常情况下,我们不手动构造一个Response实例,我们只需要了解Response的结构即可。

Response实例具有以下属性

statusHTTP响应状态码
statusTextHTTP响应状态描述
headers

HTTP响应头,headers无法直接通过.来获取响应头,而要通过get方法来获取,原因时headers是Header类型,该类实现了Symbol.iterator,是一个可迭代对象,他需要通过get方法获取指定响应头

 

bodyHTTP响应体。一个简单的 getter,用于暴露一个 ReadableStream 类型的 body 内容。
bodyUsed包含了一个布尔值 (en-US)来标示该 Response 是否读取过 Body
ok

本次响应是否成功,true成功,false失败。

判断标准是:HTTP响应状态码在200~299之间表示成功,其他表示失败

type

响应类型,有如下值:

  • basic: 标准值, 同源响应, 带有所有的头部信息除了“Set-Cookie” 和 “Set-Cookie2″.
  • cors: Response 接收到一个有效的跨域请求. 
  • error: 网络错误. 没有有用的描述错误的信息。响应的状态为0,header为空且不可变。从 Response.error()中获得的响应的类型.
  • opaque: 响应 “no-cors” 的跨域请求.
urlHTTP请求URL
redirected表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目。

其中,我们需要注意的是body属性值是一个可读流,所以我们无法直接获取body内容,需要从可读流中读取内容,而读取可读流中内容也是一个异步操作,Response贴心的为我们提供了如下实例方法去异步地获取body可读流中的内容

json()读取body内容为JSON对象
text()读取body内容为普通文本字符串
formData()读取body内容为FormData对象
blob()读取body内容为Blob对象
arrayBuffer()读取body内容为ArrayBuffer对象

以上方法都返回一个Promise对象,且Promise对象的结果值为它们读取到并转换为对应格式的数据。

async function test(){const response = await fetch('http://localhost:3000/test?name=qfc&age=18')console.log('bodyUsed:', response.bodyUsed)const body = await response.json()console.log(body)console.log('bodyUsed:', response.bodyUsed)const bodyAgain = await response.json()console.log(bodyAgain)
}test()

通过以上代码测试发现,当response.json()返回的Promise的结果值确实是body实际内容,并且自动被转化为JSON对象。bodyUsed属性在json()执行后,也从false改变为了true,表示body内容读取过了。

需要注意的是,可读流的内容只能读取一次,读取完就没了,再次读取则会报错

如果我们想进行多次读取,则可以对可读流进行克隆,然后操作克隆的可读流,具体操作如下:

async function test(){const response = await fetch('http://localhost:3000/test?name=qfc&age=18')const clone1 = response.clone()const body = await clone1.json()console.log(body)const clone2 = response.clone()const bodyAgain = await clone2.json()console.log(bodyAgain)
}test()

fetch函数的用法

前面介绍了Request和Response,我们知道了fetch可以入参一个Request对象,返回一个Response对象为结果值的Promise对象。

但是每次执行fetch,都创建一个Request对象显得有点麻烦,所以fetch函数支持如下语法:

Promise<Response> fetch(input[, init]);
fetch('http://localhost:3000/test', {method: 'post',headers: {'Content-Type': 'application/json'},body: JSON.stringify({name: 'qfc',age: 18})
}).then(response => {console.log(response)
})

即,fetch不需要入参一个标准的Request对象,而是将用于创建Request对象的入参转移到fetch函数的入参。

fetch取消请求

不同于XMLHttpRequest基于实例来取消请求,由于fetch没有提供发送AJAX的实例,所以fetch需要通过传入配置的方式,建立与底层发送AJAX的实例的联系。

浏览器原生提供了一个AbortController类,该类的原型上有一个signal属性,有一个abort函数

而fetch函数的第二个参数配置对象有一个属性signal,该属性用于接收一个AbortController实例的signal属性值,这也建立了fetch函数底层发送AJAX的实例与AbortController实例的联系。

而Abort实例通过调用abort即可引发fetch函数发送的AJAX取消。 

const controller = new AbortController()fetch('http://localhost:3000/test', {signal: controller.signal
}).then(response => {console.log(response)}).catch(err => {console.log('错误信息:', err)})setTimeout(()=>{controller.abort()}, 1000) // 服务器5s后返回

但是有一个很奇怪的地方,controller.abort是一个函数,居然不能直接作为setTimeout回调,而是需要封装到一个函数中,不知道为啥

而当controller.abort()执行后,fetch就会抛出一个DOMException,异常的name属性为AbortError

fetch的异常结果

fetch只将网络异常,如网络未接入,网络中断,服务器无法连接,当成真异常,而对于HTTP响应状态码不在200~299的情况都会被当成正常结果,而不是异常。

我们再来看下异常响应的结构

可以发现,fetch异常响应结果值就是一个TypeError对象,该对象自身有message,stack属性,原型上有一个name属性

fetch和axios的区别

二者最本质区别是

fetch是浏览器原生函数,axios是基于浏览器原生XMLhttpRequest封装的第三方库。

在请求设计上

fetch只能当成函数使用,axios不仅可以当成函数使用,也可以当成对象使用

fetch函数第一个入参不能是配置对象,axios可以是

fetch函数入参配置对象中请求体body不能直接传入普通JS对象,axios入参配置对象的data可以

在正常结果上

fetch返回的Promise结果值Response实例的body是一个可读流对象,需要异步读取

axios返回的Promise结果值中所有属性都可以同步获取

在异常结果上

fetch只将网络异常当成异常,对于服务器返回非 200~299 的HTTP响应,都看出正常结果

axios默认将非 200~299 的HTTP响应,看出是异常结果

另外fetch的异常结果对象有两种TypeError,DOMException,都是浏览器原生异常对象

而axios的异常对象都是自定义的对象,包含详细的异常信息

在取消请求上

fetch依赖于外部的AbortController实例来取消请求,通过AbortController实例的signal和fetch入参配置对象属性signal建立联系,然后通过AbortController实例调用abort方法完成取消请求。

axios依赖于外部的axios.CancelToken实例来取消请求,通过axios.CancelToken实例与axios入参配置对象属性cancelToken建立联系,然后axios内部将xhr.abort()执行权交给axios.CancelToken实例,通过axios.CancelToken再将得到的取消请求控制权再次交给用户,由用户把控取消请求


http://chatgpt.dhexx.cn/article/23ckckPn.shtml

相关文章

JavaScript fetch() 方法

JavaScript 中的fetch()方法 用于向服务器请求并加载网页中的信息。请求可以是返回 JSON 或 XML 格式数据的任何 API。此方法返回一个承诺。 句法&#xff1a; fetch( url, options ) 参数&#xff1a;此方法接受上面提到的两个参数&#xff0c;如下所述&#xff1a; URL&a…

fetch的使用方法

注意&#xff1a;fetch是一种HTTP数据请求的方式&#xff0c;是XMLHttpRequest的一种替代方案。fetch不是ajax的进一步封装&#xff0c;而是原生js内置的一种方法&#xff0c;所以无需按包&#xff0c;无需引用&#xff0c;直接使用。 1.发送get请求&#xff08;fetch默认是fe…

浏览器控制台copy as fetch用法

不解释了&#xff0c;直接上图。 1、先选择要copy的接口&#xff0c;浏览器控制台右键选中&#xff0c;复制到剪贴板 2、复制到控制台&#xff0c;大致如下。 3、去掉末尾的分号&#xff0c;添加如下&#xff1a; .then(response > response.json()).then(data >console…

fetch的用法ajax,Promise发送Ajax请求、fetch用法

实例方法&#xff1a;Document /* 基于Promise发送Ajax请求 */ function queryData(url) { var p new Promise(function(resolve, reject){ var xhr new XMLHttpRequest(); xhr.onreadystatechange function(){ if(xhr.readyState ! 4) return; if(xhr.readyState 4 &&…

vue学习笔记-接口调用fetch用法

一&#xff0c;fetch概述 二&#xff0c;fetch基本用法 第一个then是fetch的一部分&#xff0c;返回一个promise对象&#xff0c;于是可以继续用then来处理返回的结果ret。 这段代码是服务器的代码&#xff1a; //这是在创建服务器&#xff0c;通过express&#xff0c;创建名为…

Vue接口调用(一)fetch用法

Vue接口调用&#x1f525; 接口调用地址Vue接口调用&#xff08;一&#xff09;fetch用法https://blog.csdn.net/m0_55990909/article/details/123957200Vue接口调用&#xff08;二&#xff09;axios用法&#x1f525;https://blog.csdn.net/m0_55990909/article/details/1239…

Fetch使用方法

前言&#xff1a; fetch是用来取代传统的XMLHttpRequest的。 它的优点很多&#xff0c;包括链式调用的语法、返回promise等。 什么是fetch? fetch api是基于promise的设计&#xff0c;它是为了取代传统xhr的不合理的写法而生的。 WHY fetch&#xff1f; xhr请求写起来非常的混…

彻底关闭烦人的---FF新推荐

1、进入控制面板——>管理工具——>服务 找到Flash Helper Service服务 2、进入控制面板——>管理工具——>任务计划程序 找到FlashHelper TaskMachineCore的任务&#xff0c;将其删除

如何关闭flashhelper的ff新推荐广告弹窗

描述&#xff1a;如何关闭flashhelper的ff新推荐广告弹窗 步骤&#xff1a; 方法一&#xff0c;直接C盘搜索flashhelper文件&#xff0c;删除即可方法二&#xff0c;在计算机管理服务里面&#xff0c;找到flashhelper禁用

ff新推荐的关闭办法

这个是flash的广告 如果直接删除Adobe Flash Helper会导致flash的不正常使用 建议是winR在输入框中输入service&#xff0c;在service里找到 然后停用再禁用掉就可以了

“FF新推荐”猥琐的弹窗如何关闭?

自己的电脑中时常会弹出一个叫“FF新推荐”的广告弹窗&#xff0c;扰民不说显示的内容也很猥亵。 特别是在演示文档&#xff0c;开会时就特别尴尬。 1. FF新推荐是个什么鬼&#xff1f;只要你安装了Flash控件会自动更新弹窗这个莫名其妙&#xff0c;十分尴尬的弹窗&#xff0…

禁用FF新鲜事广告

不知道啥时候起&#xff0c;电脑开机之后总会弹出莫名其妙的FF新鲜事广告&#xff0c;如图 定睛一看&#xff0c;Flash助手推荐&#xff0c;原来Flash才是罪魁祸首。这不能忍。打开任务管理器&#xff0c;不停按f&#xff0c;可以找到一个叫Flash Helper Service的进程&#xf…

FF新推荐弹窗怎么彻底让他爬

背景&#xff1a;不知道怎么就捆绑安装了这个流氓软件&#xff0c;还找不到位置&#xff0c;不知道在哪删&#xff1f;今天突然灵机一动试了一下这个方法&#xff0c;然后可行 第一步&#xff1a;守株待兔——等他不经意的弹出 第二步&#xff1a;欲擒故纵——点击右上角菜单创…

git merge之--no-ff 的作用

禁止使用快进模式 no-fast—forward $ git merge --no-ff -m "merge with no-ff" dev在许多介绍 Git 工作流的文章里&#xff0c;都会推荐在合并分支时&#xff0c;加上 --no-ff 参数&#xff1a; $ git checkout develop $ git merge --no-ff feature–no-ff 在这的…

如何卸载FF推荐?

很多小伙伴们都发现&#xff0c;近一段时间&#xff0c;自己的电脑中时常会弹出一个叫“FF新推荐”的广告弹窗&#xff0c;扰民不说显示的内容也很猥亵。按理说&#xff0c;电脑上开启拦截软件后&#xff0c;一般的广告弹窗是不会出现的&#xff0c;那么这个能跳过安全工具拦截…

git merge 合并时 --no-ff 的作用——主要影响版本回退(好文章!)

在许多介绍 Git 工作流的文章里&#xff0c;都会推荐在合并分支时&#xff0c;加上 --no-ff 参数&#xff1a; $ git checkout develop$ git merge --no-ff feature --no-ff 在这的作用是禁止快进式合并。我们平常什么都不加的时候&#xff0c;则使用默认的 --ff &#xff0c…

win10如何禁用\删除讨厌的ff新鲜事(ff新推荐)(flash弹窗广告)

雾草&#xff0c;Adobe Flash植入的这种广告太变态了&#xff01; 定位&#xff1a; 打开运行窗口&#xff08;winr&#xff09;&#xff0c;输入“services.msc”&#xff0c;之后回车确定 然后我们在弹出来的窗口中找到“flash helper service”选项&#xff0c;之后右键…

#FF00FF(255,0,255)应该是紫色(Purple),把#FF00FF称为“品红”(“洋红/Magenta”)是一种误称

作者&#xff1a;❄️固态二氧化碳❄️ (主页) 链接&#xff1a;#FF00FF(255,0,255)应该是紫色(Purple),把#FF00FF称为"品红"(“洋红/Magenta”)是一种误称 - 固态二氧化碳的博客 - CSDN博客 来源&#xff1a;CSDN博客 发表时间&#xff1a;2019年06月02日 18:31:06 …

音乐推荐系统

✨写在前面&#xff1a;强烈推荐给大家一个优秀的人工智能学习网站&#xff0c;内容包括人工智能基础、机器学习、深度学习神经网络等&#xff0c;详细介绍各部分概念及实战教程&#xff0c;通俗易懂&#xff0c;非常适合人工智能领域初学者及研究者学习。➡️点击跳转到网站。…

推荐系统算法--ItemCF--MF(ALS)--FF

1.ItemCF&#xff1a; 协同过滤是什么&#xff1f; 协同过滤 (Collaborative filtering)&#xff0c;指的是&#xff0c;通过收集群体用户的偏好信息&#xff0c;自动化预测&#xff08;过滤&#xff09;个体用户可能感兴趣的内容。协同&#xff08;collaborating&#xff09…