此刻你在沙漠里,因此你要潜心于沙漠之中。沙漠和世上其他东西一样,可以用来理解世界。你甚至不必理解沙漠,只要观察普通的沙粒就行,从中你可以看到天地万物的神奇之处。——–《牧羊少年的人生之旅》
写在前面
此刻你在沙漠里,因此你要潜心于沙漠之中。沙漠和世上其他东西一样,可以用来理解世界。你甚至不必理解沙漠,只要观察普通的沙粒就行,从中你可以看到天地万物的神奇之处。——–《牧羊少年的人生之旅》
Fetch API
提供了一个获取资源的接口(包括跨域请求)。任何使用过XMLHttpRequest
的人都能轻松上手,而且新的 API 提供了更强大和灵活的功能集。
Fetch
提供了对Request
和Response
,Headers
(以及其他与网络请求有关的)对象的通用定义
fetch()
必须接受一个参数——资源的路径。无论请求成功与否,它都返回一个Promise
对象,resolve
对应请求的 Response
。你也可以传一个可选的第二个参数 init
。
一旦Response
被返回,就可以使用一些方法来定义内容的形式,以及应当如何处理内容,你也可以通过 Request() 和 Response() 的构造函数直接创建请求和响应,但是我们不建议这么做。
Fetch 接口
Headers
:相当于 response/request 的头信息
Request
:相当于一个资源请求
Response
:相当于请求的响应
使用 Fetch Fetch API
提供了一个JavaScript
接口,用于访问和操纵 HTTP 管道
的一些具体部分,例如请求和响应。它还提供了一个全局fetch()
方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。
fetch 规范与jQuery.ajax()
主要有以下的不同:
当接收到一个代表错误的 HTTP 状态码
时,从 fetch() 返回的Promise
不会被标记为 reject
,即使响应的 HTTP 状态码
是 404 或 500
。相反,它会将Promise
状态标记为 resolve
(如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject
。 +fetch
不会发送跨域 cookies
,除非你使用了credentials
的初始化选项。(自2018 年 8 月以后,默认的 credentials 政策变更为 same-origin
。Firefox 也在 61.0b13 版本中进行了修改)
fetch()
使用 Promise
,不使用回调函数
,因此大大简化了写法,写起来更简洁。
fetch()
采用模块化设计
,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象
),更合理一些;相比之下,XMLHttpRequest
的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。
fetch()通过数据流(Stream 对象)处理数据
,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。
在用法上,fetch()
接受一个URL
字符串作为参数,默认向该网址发出GET
请求,返回一个 Promise
对象。
环境准备 这里我们用Node
环境来学习,当然在浏览器更有可比性,需要安装基于Node
的依赖包node-fetch
,这里一定要注意版本问题
node-fetch
用于服务器端
,即只能在nodejs
中用
whatwg-fetch
用于客户端
,即用于在浏览器没有原生支持fetch
的情况
isomorphic-fetch
可以在nodejs
和浏览器
两种环境中运行,是对whatwg-fetch
的包装
。
1 npm install node-fetch@2
同时我们需要一个Web
服务用作测试,这里用python
搭一个简单的Web
服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 ''' @File : fetch.py @Time : 2022/03/04 18:59:02 @Author : Li Ruilong @Version : 1.0 @Contact : 1224965096@qq.com @Desc : Fetch学习Demo ''' from time import sleepfrom flask import Flask,jsonify,request,send_from_directoryimport osDEBUG = True app = Flask(__name__) @app.route("/" ) @app.route("/index" ) def default (): ''' @Time : 2022/03/04 18:58:42 @Author : Li Ruilong @Version : 1.0 @Desc : 默认页面 ''' return "<h1>Fetch学习Demo<h1/>" @app.route('/init' , methods=['GET' ] ) def init (): ''' @Time : 2022/03/04 19:41:40 @Author : Li Ruilong @Version : 1.0 @Desc : get请求返回JSON ''' data = ["Ajax" ,"Fetch" ,"Promise" ,"Axios" ] return jsonify(data) @app.route("/add" ,methods=["POST" ] ) def add (): ''' @Time : 2022/03/04 19:43:05 @Author : Li Ruilong @Version : 1.0 @Desc : Post请求 ''' data = request.json print (*data, sep='\n' ) return jsonify({"msg" :"Post请求成功" ,"code" :"0" }) @app.route("/download/<filename>" ) def download (filename ): print (filename) ''' @Time : 2022/03/04 22:30:12 @Author : Li Ruilong @Version : 1.0 @Desc : 下载文件 ''' directory = os.getcwd() print (directory) return send_from_directory(directory, filename, as_attachment=True ) @app.route('/upload' , methods=['POST' , 'PUT' ] ) def upload (): ''' @Time : 2021/12/15 10:32:03 @Author : Li Ruilong @Version : 1.0 @Desc : 上传文件 ''' if request.method == 'POST' : try : f = request.files['file' ] print ("上传的文件名:===" , f.filename) basepath = os.path.dirname(__file__) upload_path = os.path.join(basepath, "\\" , str (f.filename)) f.save(upload_path) print ("保存的文件路径:" +upload_path) except Exception as e: print ("上传文件失败" , e) return jsonify({"msg" :"上传文件OK" ,"code" :"0" }),200 @app.route("/stop/<int:s>" ) def stop (s ): sleep(s) return "OK" ,200 if __name__ == '__main__' : app.run(host='127.0.0.1' , port=37881 , debug=DEBUG)
data.json文件
1 2 3 4 5 6 7 8 9 10 11 12 [ { "site" : "npr" , "link" : "http://www.npr.org/rss/rss.php?id=1001" , "type" : "rss" }, { "site" : "npr" , "link" : "http://www.npr.org/rss/rss.php?id=1008" , "type" : "rss" } ]
准备工作做好以后,我们开始愉快的学习吧
一个基本的fetch
请求设置起来很简单。看看下面的代码:
这是一个回调风格的请求,从服务器获取JSON
数据。在Node
环境的一个Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const fetch = require ("node-fetch" );fetch('http://127.0.0.1:37881/download/data.json' ) .then(response => response.json()) .then(json => console .log(json)) .catch(err => console .log('Request Failed' , err));
fetch()
接收到的response
是一个Stream
对象,response.json()
是一个异步操作,取出所有内容,并将其转为 JSON 对象
。
整理上看和axios
类似,相同点都是基于ES 6
的Promise
对象,在Node
环境,都是基于HTTP模块
实现,不同点,axios
在浏览器中,是基于XMLHttpRequests
来实现异步通信的,而fetch
是一个新的API
,是XMLHttpRequest
的最新替代技术 ,下面是一个axios
的例子.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 const axios = require ('axios' ).default;const { v4 : uuidv4 } = require ('uuid' );let subscriptionKey = "3c6588c7026b41a4**7f81551cb4a737" ;let endpoint = "https://api.translator.azure.cn/" ;let location = "chinanorth" ;axios({ baseURL : endpoint, url : '/translate' , method : 'post' , headers : { 'Ocp-Apim-Subscription-Key' : subscriptionKey, 'Ocp-Apim-Subscription-Region' : location, 'Content-type' : 'application/json' , 'X-ClientTraceId' : uuidv4().toString() }, params : { 'api-version' : '3.0' , 'from' : 'zh-Hans' , 'to' : ['zh-Hant' , 'en' ] }, data : [{ 'text' : '我徒然学会了抗拒热闹,却还来不及透悟真正的冷清。--------张大春' }], responseType : 'json' }).then(function (response ) { console .log(JSON .stringify(response.data, null , 4 )); }).catch(function (error ) { console .log(error); });
Promise
可以使用await
语法改写,使得语义更清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/download/data.json' ; try { let response = await fetch(url); let data =await response.json(); console .log(data); } catch (e) { console .log("Oops, error" , e); } })()
await
语句必须放在try...catch
里面,这样才能捕捉异步
操作中可能发生的错误
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ===== PS D:\GolandProjects\code-master\demo> node fetch [ { site: 'npr' , link: 'http://www.npr.org/rss/rss.php?id=1001' , type : 'rss' }, { site: 'npr' , link: 'http://www.npr.org/rss/rss.php?id=1008' , type : 'rss' } ] PS D:\GolandProjects\code-master\demo>
Response 对象:处理 HTTP 回应 fetch()
请求成功以后,得到的是一个Response
对象。它对应服务器的 HTTP 回应
。
1 const response = await fetch(url);
Response
包含de同步属性,对应 HTTP 回应的标头信息(Headers)
,可以立即读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/init' ; try { let response = await fetch(url); console .log(response.ok); console .log(response.status); console .log(response.statusText); console .log(response.type); console .log(response.url); console .log(response.redirected) let data =await response.json(); console .log(data); } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 7 8 9 10 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" true 200 OK undefined http://127.0.0.1:37881/init false [ 'Ajax' , 'Fetch' , 'Promise' , 'Axios' ] [Done] exited with code=0 in 0.253 seconds
response.ok
:属性返回一个布尔值,表示请求是否成功,true对应 HTTP 请求的状态码 200 到 299,false对应其他的状态码。
response.status
:属性返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。
response.statusText
:属性返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回”OK”)。
response.url
:属性返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。
response.type
:属性返回请求的类型。可能的值如下:
通过状态码判断请求是否成功 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/init' ; try { let response = await fetch(url); if (response.status >= 200 && response.status < 300 ){ let data = await response.json(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
我们把python的web服务接口里抛出一个异常,直接到了catch里面
1 2 3 4 5 6 7 8 9 10 11 12 @app.route('/init' , methods=['GET' ] ) def init (): ''' @Time : 2022/03/04 19:41:40 @Author : Li Ruilong @Version : 1.0 @Desc : get请求返回JSON ''' data = ["Ajax" ,"Fetch" ,"Promise" ,"Axios" ] raise Exception('这是一个请求异常的模拟' ) return jsonify(data)
执行报错:内部服务器错误,即500
1 2 3 4 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" Oops, error Error: INTERNAL SERVER ERROR at d:\GolandProjects\code-master\demo\fetch.js:23:15 at processTicksAndRejections (internal/process/task_queues.js:93:5)
修改接口返回状态码为400
1 2 3 4 5 6 7 8 9 10 11 @app.route('/init' , methods=['GET' ] ) def init (): ''' @Time : 2022/03/04 19:41:40 @Author : Li Ruilong @Version : 1.0 @Desc : get请求返回JSON ''' data = ["Ajax" ,"Fetch" ,"Promise" ,"Axios" ] return jsonify(data),400
报错误请求
1 2 3 4 5 6 7 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" INTERNAL SERVER ERROR Oops, error Error: INTERNAL SERVER ERROR at d:\GolandProjects\code-master\demo\fetch.js:24:19 at processTicksAndRejections (internal/process/task_queues.js:93:5) [Done] exited with code=0 in 0.261 seconds
也可以直接通过response.ok
来判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/init' ; try { let response = await fetch(url); if (response.ok){ let data = await response.json(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
修改接口返回状态码为404
1 2 3 4 5 6 7 8 9 10 11 @app.route('/init' , methods=['GET' ] ) def init (): ''' @Time : 2022/03/04 19:41:40 @Author : Li Ruilong @Version : 1.0 @Desc : get请求返回JSON ''' data = ["Ajax" ,"Fetch" ,"Promise" ,"Axios" ] return jsonify(data),404
1 2 3 4 5 6 7 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" NOT FOUND Oops, error Error: NOT FOUND at d:\GolandProjects\code-master\demo\fetch.js:24:19 at processTicksAndRejections (internal/process/task_queues.js:93:5) [Done] exited with code=0 in 0.257 seconds
Response
对象还有一个Response.headers
属性,指向一个Headers
对象,对应 HTTP
回应的所有标头。
Headers
对象可以使用for...of
循环进行遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/init' ; try { let response = await fetch(url); if (response.ok){ let data = await response.json(); console .log(data); for (let [key, value] of response.headers) { console .log(`${key} : ${value} ` ); } return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 7 8 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" [ 'Ajax' , 'Fetch' , 'Promise' , 'Axios' ] content-length : 51 content-type : application/json date : Sat, 05 Mar 2022 15:14:47 GMT server : Werkzeug/2.0.2 Python/3.9.0 [Done] exited with code=0 in 0.26 seconds
Headers
对象提供了以下方法,用来操作标头。HTTP 回应来说,修改标头意义不大
Headers.get()
:根据指定的键名,返回键值。
Headers.has()
: 返回一个布尔值,表示是否包含某个标头。
Headers.set()
:将指定的键名设置为新的键值,如果该键名不存在则会添加。
Headers.append()
:添加标头。
Headers.delete()
:删除标头。
Headers.keys()
:返回一个遍历器,可以依次遍历所有键名。
Headers.values()
:返回一个遍历器,可以依次遍历所有键值。
Headers.entries()
:返回一个遍历器,可以依次遍历所有键值对([key, value])。
Headers.forEach()
:依次遍历标头,每个标头都会执行一次参数函数。
读取内容的方法 Response
对象根据服务器返回的不同类型的数据,提供了不同的读取方法。读取方法都是异步的
,返回的都是 Promise 对象。必须等到
异步操作结束,才能得到服务器返回的
完整数据`。
response.text()
:得到文本字符串。
response.json()
:得到 JSON 对象。
response.blob()
:得到二进制 Blob 对象。
response.formData()
:得到 FormData 表单对象。
response.arrayBuffer()
:得到二进制 ArrayBuffer 对象。
response.text()
可以用于获取文本数据,比如HTML
文件。
1 2 3 4 5 6 7 8 9 10 @app.route("/" ) @app.route("/index" ) def default (): ''' @Time : 2022/03/04 18:58:42 @Author : Li Ruilong @Version : 1.0 @Desc : 默认页面 ''' return "<h1>Fetch学习Demo<h1/>"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/' ; try { let response = await fetch(url); if (response.ok){ let data = await response.text(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
response.json()
主要用于获取服务器返回的 JSON
数据
response.formData()
主要用在 Service Worker 里面,拦截用户提交的表单,修改某些数据以后,再提交给服务器。
response.blob()
用于获取二进制文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/download/data.json' ; try { let response = await fetch(url); if (response.ok){ let data = await response.blob(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 7 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" Blob { [Symbol(type )]: 'application/json' , [Symbol(buffer)]: <Buffer 5b 0a 09 7b 0a 09 09 22 73 69 74 65 22 3a 20 22 6e 70 72 22 2c 0a 09 09 22 6c 69 6e 6b 22 3a 20 22 68 74 74 70 3a 2f 2f 77 77 77 2e 6e 70 72 2e 6f 72 ... 141 more bytes> } [Done] exited with code=0 in 0.847 seconds
response.arrayBuffer()主要用于获取流媒体文件。
1 2 3 4 5 6 7 8 9 10 const audioCtx = new window .AudioContext();const source = audioCtx.createBufferSource();const response = await fetch('song.ogg' );const buffer = await response.arrayBuffer();const decodeData = await audioCtx.decodeAudioData(buffer);source.buffer = buffer; source.connect(audioCtx.destination); source.loop = true ;
Response.clone() Stream 对象
只能读取一次,读取完就没了。这意味着,前一节的五个读取方法,只能使用一个,否则会报错。
Response
对象提供Response.clone()
方法,创建Response对象的副本
,实现多次读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/download/data.json' ; try { let response = await fetch(url); let response1 = response.clone(); if (response.ok){ let data = await response.json(); let data1 = await response1.json() console .log(data1); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" [ { site: 'npr' , link: 'http://www.npr.org/rss/rss.php?id=1001' , type : 'rss' }, { site: 'npr' , link: 'http://www.npr.org/rss/rss.php?id=1008' , type : 'rss' } ] [Done] exited with code=0 in 0.25 seconds
Response 对象还有一个Response.redirect()
方法,用于将 Response 结果重定向到指定的 URL。该方法一般只用在 Service Worker 里面
Response.body 属性 Response.body
属性是 Response 对象暴露出的底层接口,返回一个 ReadableStream
对象,供用户操作。
它可以用来分块读取内容,应用之一就是显示下载的进度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const fetch = require ("node-fetch" );(async () => { let url = 'http://127.0.0.1:37881/download/data.json' ; fetch(url) .then(response => response.body) .then(res => res.on('readable' , () => { let chunk; while (null !== (chunk = res.read())) { console .log(chunk.toString()); } })) .catch(err => console .log(err)); })()
第二个参数init:定制 HTTP 请求 fetch()
的第一个参数是 URL
,还可以接受第二个参数
,作为配置对象
,定制发出的HTTP 请求
。
HTTP
请求的方法、标头、数据体都在这个对象里面设置
Post请求传递JSON 1 2 3 4 5 6 7 8 9 10 11 @app.route("/add" ,methods=["POST" ] ) def add (): ''' @Time : 2022/03/04 19:43:05 @Author : Li Ruilong @Version : 1.0 @Desc : Post请求 ''' data = request.json print (*data, sep='\n' ) return jsonify({"msg" :"Post请求成功" ,"code" :"0" })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const fetch = require ("node-fetch" );(async () => { const url = 'http://127.0.0.1:37881/add' ; const body = { name : 'John' , surname : 'Smith' }; try { let response = await fetch(url,{ method : 'post' , body : JSON .stringify(body), headers : {'Content-Type' : 'application/json;charset=utf-8' } }); if (response.ok){ const data = await response.json(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 7 8 name surname 127.0.0.1 - - [06/Mar/2022 02:27:42] "POST /add HTTP/1.1" 200 - ======== [Running] node "d:\GolandProjects\code-master\demo\fetch.js" { code: '0' , msg: 'Post请求成功' } [Done] exited with code=0 in 0.293 seconds
文件上传 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @app.route('/upload' , methods=['POST' , 'PUT' ] ) def upload (): ''' @Time : 2021/12/15 10:32:03 @Author : Li Ruilong @Version : 1.0 @Desc : 上传文件 ''' if request.method == 'POST' : try : f = request.files['file' ] print ("上传的文件名:===" , f.filename) basepath = os.path.dirname(__file__) upload_path = os.path.join(basepath, "\\" , str (f.filename)) f.save(upload_path) print ("保存的文件路径:" +upload_path) except Exception as e: print ("上传文件失败" , e) return jsonify({"msg" :"上传文件OK" ,"code" :"0" }),200
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 const fs = require ('fs' ); const fetch = require ('node-fetch' ); const FormData = require ('form-data' ); let fileStream = fs.readFileSync("./data.json" ); let formdata = new FormData(); const mimetype = 'text/plain' formdata.append("file" , fileStream, { filename : "./data.json" , contentType : mimetype, }); (async () => { const url = 'http://127.0.0.1:37881/upload' ; try { let response = await fetch(url,{ method : 'post' , body : formdata , headers : formdata.getHeaders() }); if (response.ok){ const data = await response.json(); console .log(data); return data; }else { console .log(response.statusText); throw new Error (response.statusText); } } catch (e) { console .log("Oops, error" , e); } })()
1 2 3 4 5 6 上传的文件名:=== data.json 保存的文件路径:d:\data.json 127.0.0.1 - - [06/Mar/2022 01:37:51] "POST /upload HTTP/1.1" 200 - ============ [Running] node "d:\GolandProjects\code-master\demo\fetch.js" { code: '0' , msg: '上传文件OK' }
fetch()配置对象的完整 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const response = fetch(url, { method : "GET" , headers : { "Content-Type" : "text/plain;charset=UTF-8" }, body : undefined , referrer : "about:client" , referrerPolicy : "no-referrer-when-downgrade" , mode : "cors" , credentials : "same-origin" , cache : "default" , redirect : "follow" , integrity : "" , keepalive : false , signal : undefined });
取消fetch()请求 fetch()
请求发送以后,如果中途想要取消,需要使用AbortController
对象。
1 2 3 4 @app.route("/stop/<int:s>" ) def stop (s ): sleep(s) return "OK" ,200
请求进去睡眠10s
,在5s
的时候终止请求
,新建AbortController
实例,然后发送fetch()
请求,配置对象的signal
属性必须指定接收AbortController
实例发送的信号controller.signal
。
controller.abort()
方法用于发出取消信号。这时会触发abort事件
,这个事件可以监听,也可以通过controller.signal.aborted
属性判断取消信号是否已经发出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const fetch = require ('node-fetch' ); const AbortController = globalThis.AbortController || require ('abort-controller' ) const controller = new AbortController(); const timeout = setTimeout (() => { controller.abort(); }, 5000 ); (async () => { const url = 'http://127.0.0.1:37881/stop/10' ; try { const response = await fetch(url, {signal : controller.signal}); const data = await response.text(); console .log(data) } catch (error) { console .log('request was aborted' ,error); } finally { clearTimeout (timeout); } })()
1 2 [Running] node "d:\GolandProjects\code-master\demo\fetch.js" request was aborted AbortError: The user aborted a request.
Node 环境
1 2 3 4 5 PS D:\GolandProjects\code-master\demo> node -v v12.13.1 PS D:\GolandProjects\code-master\demo> npm -v 6.12.1 PS D:\GolandProjects\code-master\demo> npm init -y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "name" : "demo" , "version" : "1.0.0" , "description" : "" , "main" : "fetch.js" , "dependencies" : { "abort-controller" : "^3.0.0" , "form-data" : "^4.0.0" , "node-fetch" : "^2.6.7" }, "devDependencies" : {}, "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" }, "keywords" : [], "author" : "" , "license" : "ISC" }
python 环境
1 2 3 4 5 6 7 8 9 PS D:\GolandProjects\code-master> python -V Python 3.9.0 PS D:\GolandProjects\code-master> pip -V pip 20.2.3 from d:\python\python310\lib\site-packages\pip (python 3.9) PS E:\docker> flask --version Python 3.9.0 Flask 2.0.2 Werkzeug 2.0.2 PS E:\docker>