目录
使用更现代的fetch API代替使用XMLHttpRequest对象
在学习图灵学院前端代码时,看到了promise异步编排解决异步请求问题,所以总结以下文章来串通个人知识点;
promise异步编排解决了什么问题:
Promise 异步编排解决了一系列与 JavaScript 中异步操作相关的问题,这些问题在早期 JavaScript 中使用回调函数时尤为突出。以下是 Promise 异步编排解决的一些主要问题:
回调地狱(Callback Hell):
- 在早期的 JavaScript 中,异步操作通常通过嵌套的回调函数来处理,这导致了所谓的“回调地狱”,代码难以阅读和维护。Promise 通过链式调用
.then()
方法提供了一种更扁平化和易于管理的方式来处理异步操作。错误处理:
- 回调函数中的错误处理通常比较复杂,而且容易遗漏。Promise 提供了
.catch()
方法,允许你集中处理所有异步操作中的错误,而不是在每个回调中单独处理。代码的可读性和可维护性:
- Promise 使得异步代码的写法更接近同步代码,提高了代码的可读性和可维护性。
异步操作的顺序执行:
- 使用 Promise,你可以轻松地按顺序执行异步操作,每个操作的输出可以作为下一个操作的输入。
状态管理:
- Promise 对象代表了一个异步操作的最终完成(或失败)及其结果值。这使得跟踪和管理异步操作的状态变得更加简单。
资源清理和最终处理:
- Promise 提供了
finally()
方法,允许你在异步操作完成后执行清理工作,无论操作是成功还是失败。
案例演示
这是早期的Ajax发送异步请求代码(代码来自图灵学院前端代码):
这是通过promise异步编排优化后代码:(两者效果一模一样,但是第二种显然比第一种更加友好)
异步编排解释我放到后面,我先回忆一下早期Ajax代码;
早期Ajax请求代码:
//new XMLHttpRequest对象
var xhr = new XMLHttpRequest();
//调用回调函数
xhr.onreadystatechange = function(){
if (this.readyState == 4) {
if (this.status == 200) {
var jsonobj = JSON.parse(this.responseText);
jsonstr.success(jsonobj)
} else {
alert(this.status)
}
}
}
//打开通道并传入必要参数(请求方式、url、是否支持异步)
xhr.open("method"," url",async)
//发送数据
xhr.send();
readyState和status解释:
在Ajax中,正确的术语是readyState和status。readyState指XMLHttpRequest对象的当前状态,其值从0到4变化,表示请求的不同阶段(初始化、载入数据、处理数据、完成请求)。而status则是HTTP响应的状态码,表示服务器返回的状态信息,例如200表示成功,404表示未找到请求的资源。
使用Ajax发送get请求案例:
//1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
//使用xhr对象调用回调函数
xhr.onreadystatechange=function (){
if (this.readyState == 4) {
if (this.status == 200) {
document.getElementById("div1").innerText=this.responseText
}else{
alert(this.status)
}
}
}
let value = document.getElementById("ipt1").value;
//打开通道
xhr.open("get","/ajax/ajaxrequest1?value="+value,true)
//发送数据
xhr.send()
使用Ajax发送post请求案例:
//1.创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
//使用xhr调用回调函数
xhr.onreadystatechange=function (){
if (this.readyState == 4) {
if (this.status == 200) {
document.getElementById("div2").innerText=this.responseText
}else{
alert(this.status)
}
}
}
//打开通道
xhr.open("post","/ajax/ajaxrequest1",true)
//为post请求添加响应头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
let value1 = document.getElementById("ipt1").value;
//发送数据
xhr.send("username="+value1)
为什么在Ajax中需要为post请求设置响应头,而get请求不需要
在Ajax中,POST请求和GET请求都需要设置响应头。无论请求方式是POST还是GET,服务器端都需要在响应头中设置Content-Type和其他相关的头信息来告知客户端返回的数据类型和编码方式。然而,由于POST请求通常会传递更多的数据,因此在处理POST请求时更加需要注意请求头和响应头的设置。
使用更现代的fetch
API代替使用XMLHttpRequest
对象
在JavaScript中,发送Ajax请求通常使用XMLHttpRequest
对象或者更现代的fetch
API。fetch
API是基于Promise设计的,它提供了一个更简洁和强大的方式来进行网络请求。以下是使用fetch
API发送Ajax请求的基本示例,它符合ES6标准:
GET请求
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json(); // 或者 response.text() 如果返回的是文本
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
POST请求
fetch('https://api.example.com/data', {
method: 'POST', // 或者 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
key1: 'value1',
key2: 'value2',
}),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json(); // 或者 response.text() 如果返回的是文本
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
/*
Response(响应):
response是指从服务器返回的整个HTTP响应对象。
它包含了HTTP状态码、响应头、响应体(body)等信息。
response对象提供了多种方法来处理响应体,例如.json()、.text()、.blob()等,这些方法用于将响应体解析成不同的格式(JSON、文本、二进制数据等)。
response对象还包含了一些属性,如status(HTTP状态码),ok(布尔值,表示状态码是否在200-299之间),headers(响应头信息)等。
Data(数据):
data是指经过处理的响应体内容。
它是response对象中的响应体经过解析后的结果,例如,如果响应体是JSON格式的,使用response.json()方法解析后,解析出来的JavaScript对象就是data。
data是应用层真正需要处理的信息,比如API返回的用户信息、商品列表等。
*/
注意事项
-
错误处理:
fetch
不会在网络请求失败时抛出错误,只有当HTTP状态码表明请求未成功时(如404或500),才会触发.catch()
。网络错误(如断网)会导致fetch
promise被拒绝。 -
Headers:在发送POST请求时,通常需要设置请求头
Content-Type
。 -
Body:对于POST请求,你需要将数据序列化为JSON字符串。
-
Async/Await:你还可以使用
async/await
语法来使异步代码看起来更像同步代码:
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There has been a problem with your fetch operation:', error);
}
}
// 使用
fetchData('https://api.example.com/data');
使用fetch
API可以简化Ajax请求的处理,并且它内置于现代浏览器中,不需要额外的库。
使用promise异步编排
Promise 异步编排是指使用 Promise 对象来管理和组织 JavaScript 中的异步操作。Promise 是一种用于异步编程的抽象,它代表了一个异步操作的最终完成(或失败)及其结果值。
Promise 可以通过Promise 构造函数创建:
let myPromise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功 */) {
resolve(value); // 将 Promise 状态置为成功,并返回 value
} else {
reject(error); // 将 Promise 状态置为失败,并返回 error
}
});
3. 处理 Promise
- .then():用于指定当 Promise 成功时的回调函数。
- .catch():用于指定当 Promise 失败时的回调函数。
- .finally():无论 Promise 成功还是失败,都会执行的回调函数。
myPromise
.then(result => {
// 处理成功的结果
})
.catch(error => {
// 处理错误
})
.finally(() => {
// 无论成功还是失败都会执行
});
使用案例(必看,不然理解会不到位):
fetch("https://www.baidu.com")//发送了一个get请求
.then(response =>{
if(!response.ok){
throw new error("网络请求失败");
}
return response.json();//使用json()方法解析响应数据,返回Data对象
})//获取响应数据
.then(data => console.log(data))//只有上面的then方法成功,才会执行这里的then方法
.catch(error => console.log(error));//如果get请求失败,处理错误
}
4. 链式调用
Promise 的 .then()
和 .catch()
方法返回一个新的 Promise,这允许你进行链式调用。
myPromise
.then(result1 => {
// 处理 result1
return anotherPromise; // 返回另一个 Promise
})
.then(result2 => {
// 处理另一个 Promise 的结果
});
回到案例:
可以看到这里是创建了一个promise对象,这个对象是用来管理和处理异步请求的内置对象,实际操作中我们并不需要多余的去创建promise对象,来冗余操作,因为在更加现代的fetchAPI 中它隐式的为我们已经创建多promise对象了,我们无需创建,开箱即用即可;
fetch("https://www.baidu.com")//发送了一个get请求
.then(response =>{
if(!response.ok){
throw new error("网络请求失败");
}
return response.json();//使用json()方法解析响应数据,返回Data对象
})//获取响应数据
.then(data => console.log(data))//只有上面的then方法成功,才会执行这里的then方法
.catch(error => console.log(error));//如果get请求失败,处理错误
}
promise对象被隐式的创建在fetch中了;类似下面这样:
function fetch(url) {
// 创建一个新的 Promise 对象
return new Promise((resolve, reject) => {
// 模拟一个异步操作,比如网络请求
setTimeout(() => {
try {
// 假设我们成功获取到了数据
const data = { message: 'This is mock data from mockFetch' };
resolve(data); // 请求成功,调用 resolve
} catch (error) {
reject(error); // 请求失败,调用 reject
}
}, 1000); // 模拟网络延迟
});
}
// 使用 fetch
fetch('https://api.example.com/data')
.then(response => {
console.log('Response:', response);
})
.catch(error => {
console.error('Error:', error);
});
在这个示例中:
Fetch
函数接受一个url
参数,并返回一个新的 Promise 对象。- 在 Promise 构造函数中,我们有两个回调函数:
resolve
和reject
。resolve
在请求成功时被调用,模拟成功获取数据。reject
在请求失败时被调用,模拟请求失败的情况。
- 我们使用
setTimeout
来模拟异步操作,比如网络请求。在延迟后,我们调用resolve
或catch
来模拟请求的成功或失败。 - 最后,我们使用
.then()
和.catch()
方法来处理 Promise 的解决和拒绝状态。
这个示例展示了如何手动创建和处理 Promise,帮助你更好地理解 fetch
API 背后的工作原理。在实际开发中,fetch
API 已经为你处理了这些细节,你只需要关注如何使用它来发送请求和处理响应。
function myAjax(url) {
return new Promise((resolve, reject) => {
$ajax({
url,
success: function (result) {//浏览器处理完请求后,回调success函数,并将结果作为参数传递result
resolve(result);
},
error: function (error) {//如果发生错误,回调error函数,并将错误对象作为参数传递error
reject(error);
}
})
})
}
参数
url
:这是传递给myAjax
函数的参数,表示要请求的 URL 地址。
Promise 构造函数
new Promise((resolve, reject) => { ... })
:这里创建了一个新的 Promise 对象。Promise 构造函数接受一个执行器函数,该函数有两个参数:resolve
和reject
。resolve
:当异步操作成功时调用,将 Promise 的状态从pending
变为fulfilled
,并返回操作的结果。reject
:当异步操作失败时调用,将 Promise 的状态从pending
变为rejected
,并返回操作的错误。
jQuery 的 AJAX 请求
$.ajax
:这是 jQuery 提供的 AJAX 请求方法,它接受一个配置对象,包括url
、success
和error
等属性。url
:请求的 URL 地址,这里直接使用myAjax
函数的参数url
。success
:当请求成功时调用的回调函数,它接收一个参数result
,表示服务器返回的数据。error
:当请求失败时调用的回调函数,它接收一个参数error
,表示错误信息。
成功和失败的处理
success: function (result) { resolve(result); }
:当 AJAX 请求成功时,jQuery 会调用success
回调函数,并传递服务器返回的数据result
。在这个回调函数中,调用resolve(result)
将 Promise 的状态设置为fulfilled
,并传递result
作为结果。这样,任何附加在myAjax
返回的 Promise 上的.then()
方法都会被调用,并接收result
作为参数。error: function (error) { reject(error); }
:当 AJAX 请求失败时,jQuery 会调用error
回调函数,并传递错误信息error
。在这个回调函数中,调用reject(error)
将 Promise 的状态设置为rejected
,并传递error
作为错误信息。这样,任何附加在myAjax
返回的 Promise 上的.catch()
方法都会被调用,并接收error
作为参数。