Fetch API 是现代浏览器提供的 JavaScript 网络请求接口,用来替代老式的 XMLHttpRequest,让前端发 HTTP 请求更简单高效。它不需要依赖任何外部的库,操作起来简单又方便。
从此发送异步请求再也不需要jQuery结合ajax了。
自定义http-request-functions.js文件
/* * 介绍:通过fetch api实现原生javascript发送http异步请求。此js文件内定义了各种常用的http请求函数。 * 作者:napster * 最后一次更新时间:2026-07-04 */ /**** *<pre> * 介绍: * 发送键值对数据的http请求。发送请求时,http协议报文内携带的内容为一个或多个键值对。 * * * * 注意: * Content-Type:application/x-www-form-urlencoded;charset=UTF-8 。 * * * * 参数: * url:请求地址。 * * key_value_pair_object:一个键值对对象,即一个包含了任意个键值对数据的js对象。 * 例如: * const user = { * username: "zhangsan", * age: 16, * address: "北京" * }; * * * * 补充: * 调用方式: * ①使用async/await的调用方式: * 示例: * async function queryOneUser() { * try { * const result = await kvpHttpRequest('/api/xxx', {id: 1}); * console.log('在外部拿到了返回的结果:', result); * } * catch(error) { * alert(error.message); * } * } * 解释: * 因为底层 fetch 发送的请求是异步的,当代码执行 fetch(...) 时,它是不会停下来等待服务器响应的,而是直接往下走,故整个函数在 fetch 还没拿到结果时就已经执行完毕了, * 所以调用时,必须加上 await 进行等待,才能得到最终的返回值。故调用时必须加上 await 关键字,若在某个方法中使用 await 关键字,则该方法必须加上 async 关键字。 * * ②使用Promise的链式调用.then().catch()方式: * 示例: * kvpHttpRequest('/api/xxx', {id: 1}) * .then(function(data) { * console.log('在外部拿到了返回的结果', data); * }) * .catch(function(error) { * alert(error.message); * }); *</pre> *****/ function kvpHttpRequest(url, key_value_pair_object) { /* * 检查空值: * ①null * ②undefined(比如调用函数时,压根没传第二个参数) * 检查类型: * 判断传入的参数是不是一个真正的“对象”。防止不小心传了一个数字、字符串、布尔值。 * 检查类型: * 判断传入的参数是不是一个数组,由于数组本身也是对象,所以要防止传入。 */ if (!key_value_pair_object || typeof key_value_pair_object !== 'object' || Array.isArray(key_value_pair_object)) { const error = new Error("方法参数值异常,请传入有效的“键值对”对象"); //创建一个错误对象 alert(error.message); //弹窗提示 throw error; //抛出 } console.log('准备发送请求'); //1、创建一个用于封装请求参数的对象URLSearchParams const urlSearchParams = new URLSearchParams(); //2、将键值对对象转换为二维数组后进行迭代,然后把迭代出来的每一项都存入URLSearchParams对象中 /* * 通过Object.entries(...)可以将一个封装了键值对数据的js对象转换为一个包含“[key, value]”的二维数组,其内部的每一项都是一个包含两个元素的数组。 * 例如: * 已知js对象: * const user = { * username: "zhangsan", * age: 16, * address: "北京" * }; * 转换后得到的二维数组: * [ * ["username", "zhangsan"], * ["age", 16], * ["address", "北京"] * ]; */ Object.entries(key_value_pair_object).forEach(function(item) { //得到的item是一个数组,格式为 [key, value] const key = item[0]; const value = item[1]; urlSearchParams.append(key, value); }); //3、通过fetch发起请求。注意:把fetch以及后续的.then()链条整体都return回去,这样调用者才能得到返回的Promise对象 return fetch(url, { method: 'POST', body: urlSearchParams }) .then(function(response) { //fetch请求只有在断网时才会报错,哪怕是服务器响应了404或500,它依然会进入这里的.then()。 /* * response对象: * 介绍: * 该对象不是服务器直接传给前端的“原始业务数据(如json对象或字符串)”,而是fetch api接口封装的一个http响应元数据对象(即Response对象)。 * 可以把它理解为一个“包裹”或“信封”,它包含了关于这次网络请求的所有状态信息和头部信息,而真正的“业务数据(ResponseBody)”需要从这个对象中进一步提取。 * 它主要包含,响应状态与元数据,响应体流这两类信息。 * response对象 = http响应(包含状态码、头信息、数据流入口)。 * * 属性: * 响应状态与元数据: * ok:布尔值,如果状态码在200-299之间(表示成功),则为true。状态码在400及以上(比如404找不到资源、500服务器报错等),则为false。 * status:数字值,表示具体的状态码。 * headers:一个Headers对象,包含服务器返回的所有响应头(如Content-Type、Set-Cookie等)。 * url:一个字符串,表示最终的url地址(如果有重定向,它就是重定向后的地址)。 * 响应体流: * body:一个ReadableStream(可读流),真正的数据(json、文本、图片二进制等)就在这里面,但它是流式的,不能直接读取。 * * 方法: * 介绍: * 该对象提供的方法返回的都是Promise对象,这是因为该对象中的响应体是以数据流(Stream)的形式接收的,而读取和解析这个数据流是一个异步操作。Promise正是用来处理这种异步操作的JavaScript标准机制。 * 当服务器开始返回响应时,数据是分批(chunk)传输的,而不是一次性全部到达。这些方法需要等待所有数据块接收完毕,然后才能进行整体解析,而这个“等待+解析”的过程天然就是异步的。 * 返回Promise使得主线程可以继续处理其他任务,数据准备好后再通过Promise的.then()或async/await来处理。 * * 常用: * json():将响应体流解析为json字符串。 * text():将响应体流解析为纯文本。 * blob():将响应体流解析为二进制大对象,Blob对象。常用于图片、文件下载、音频视频等场景。 * formData():将响应体流解析为表单数据,FormData对象。常用于处理 multipart/form-data响应场景。 * arrayBuffer():将响应体流解析为原始二进制缓冲区,ArrayBuffer对象。 * * 总结: * 这些方法本身不直接返回数据,而是返回一个“承诺”(Promise),这个“承诺”在未来会给你数据。 * * 注意: * 该对象的body流只能被读取一次,一旦调用了response.json()或response.text(),那么流就被消耗完了。 * 如果你需要同时用两种方式读取(比如既要看文本又要看json),必须先调用response.clone()创建一份副本。 * */ if(response.ok) { return response.json(); //调用response对象提供的json()方法,把响应体流解析成json格式字符串,return的结果传递给下一个then() } throw new Error("响应状态码:" + response.status); }) .then(function(data) { console.log('服务器响应的数据为:', data); return data; }) .catch(function(error) { //抓取错误,这里除了可以抓取之前的.then()抛出的Error对象以外,还可以抓取到因为断网而导致的失败对象,甚至还可以抓取到响应体流解析失败的错误 alert(error.message); //弹窗提示 throw error; //抛出错误,由外部调用者决定如何处理 }); } /**** *<pre> * 介绍: * 发送json数据的http请求。发送请求时,http协议报文内携带的内容为一个json字符串。 * * * * 注意: * Content-Type:application/json 。 * * * * 参数: * url:请求地址。 * * json_string:一个json字符串,例如:{"username":"zhangsan", "age":16, "address":"北京"} 。 * * * * 知识: * json的官方规范(RFC 8259): * ①json内的元素的“键”必须加双引号 "" ,单引号或者不加引号都是非法的。 * ②json内的元素的“值”,如果为字符串类型的话,那么必须用双引号 "" 包裹,千万不能使用单引号 '' ,否则也是非法的。 * ③json内的元素的“值”,不支持undefined,只能是字符串、数字、布尔值、null、对象、数组。 * ④json内不能包含方法或函数,json只能表示“纯数据”。 * ⑤json内不能包含代码注释,标准json不支持注释。 * * json和js对象的区别: * js对象的本质为一段可执行的js代码,而json的本质为一种纯文本的数据交换格式。 * * json和js对象互转: * 方法: * JSON.stringify(...) :将一个js对象(或数组)“转换(序列化)”为一个“标准”的json字符串。 * JSON.parse(...) :将一个json字符串“解析(反序列化)”为一个“js对象”。 * * 示例: * 已知js对象: * const user = { * username: "zhangsan", * age: 16, * address: "北京" * }; * 转换为json字符串: * {"username":"zhangsan", "age":16, "address":"北京"} 。 * * 注意: * 在使用“JSON.stringify(...)”时,有以下几样东西是无法被转换的: * ①函数,如果一个js对象内含有函数的话,转换时会被直接忽略丢弃。 * ②undefined值,会被直接忽略丢弃。 * ③Symbol,会被直接忽略丢弃。Symbol是ES6(2015年)引入的一种全新的基本数据类型。 * 示例: * 已知js对象: * const obj = { * name: "zhangsan", * age: undefined, * sayHi: function() { console.log("Hi"); } * }; * 转换为json字符串: * {"name":"zhangsan"} *</pre> *****/ function jsonHttpRequest(url, json_string) { console.log('准备发送请求'); return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, //必需要有这个请求头 body: json_string }) .then(function(response) { if(response.ok) { return response.json(); } throw new Error("响应状态码:" + response.status); }) .then(function(data) { console.log('服务器响应的数据为:', data); return data; }) .catch(function(error) { alert(error.message); throw error; }); } /**** *<pre> * 介绍: * 发送form表单数据的http请求。发送请求时,http协议报文内携带的内容为form表单要提交的数据,包括文件上传。 * * * * 注意: * Content-Type:multipart/form-data; boundary=---- 。 * * * * 参数: * url:请求地址。 * * form_element:一个form元素(表单元素)。 * * * * 原理: * 将整个form表单要提交的数据,封装为一个FormData对象,最后在使用fetch发送http请求时通过构造参数对象传递过去,从而实现发送“携带参数的http请求”。 * * FormData对象: * ①它是js的原生对象,所以不用依赖其它库。 * ②它会自动收集表单内的各种控件的数据,例如:select、textarea、input、text、radio、checkbox、file、date、number 等。 * ③它会自动忽略disable、无name属性的控件。 * ④支持文件上传 *</pre> *****/ function multipartFormDataHttpRequest(url, form_element) { console.log('准备发送请求'); const formData = new FormData(form_element); //自动把表单要提交的数据封装成FormData return fetch(url, { method: 'POST', body: formData }) .then(function(response) { if(response.ok) { return response.json(); } throw new Error("响应状态码:" + response.status); }) .then(function(data) { console.log('服务器响应的数据为:', data); return data; }) .catch(function(error) { alert(error.message); throw error; }); } /**** *<pre> * 介绍: * 发送form表单数据的http请求。发送请求时,http协议报文内携带的内容为form表单要提交的数据,但不包括文件上传。 * * * * 注意: * Content-Type:application/x-www-form-urlencoded;charset=UTF-8 。 * * * * 参数: * url:请求地址。 * * form_element:一个form元素(表单元素)。 * * * * 原理: * 将整个form表单要提交的数据,封装为一个FormData对象,然后再封装为URLSearchParams对象,最后在使用fetch发送http请求时通过构造参数对象传递过去,从而实现发送“携带参数的http请求”。 * * URLSearchParams对象: * ①它是js的原生对象,所以不用依赖其它库。 * ②它会自动收集表单内的各种控件的数据,例如:select、textarea、input、text、radio、checkbox、date、number 等。 * ③它会自动忽略disable、无name属性的控件。 * ④不支持文件上传。 *</pre> *****/ function simpleFormDataHttpRequest(url, form_element) { console.log('准备发送请求'); const formData = new FormData(form_element); //自动把表单要提交的数据封装成FormData const urlSearchParams = new URLSearchParams(formData); return fetch(url, { method: 'POST', body: urlSearchParams }) .then(function(response) { if(response.ok) { return response.json(); } throw new Error("响应状态码:" + response.status); }) .then(function(data) { console.log('服务器响应的数据为:', data); return data; }) .catch(function(error) { alert(error.message); throw error; }); }