引言
在如今这个追求极致用户体验的前端开发时代,页面加载速度
和数据获取效率
堪称是前端工程师的"命根子"。想象一下,当用户打开一个页面,需要同时获取用户信息、商品列表、推荐内容等多个数据接口时,一个个顺序发送请求,那等待时间简直能把用户"劝退"!而fetch API
作为现代前端开发中数据请求
的"主力军",如何用它实现并发请求
,并优雅处理可能出现的错误,就成了每个前端工程师必须掌握的硬核技能。今天,咱就用大白话唠一唠,10行代码就能搞定的fetch
并发请求及错误处理的神操作!
一、什么是fetch API?为啥它这么火?
fetch API
是浏览器提供的一个用于网络请求
的接口,相比老一代的XMLHttpRequest
(简称XHR
),它使用起来更加简洁、直观,而且支持Promise
,这对于喜欢用async/await
语法糖的前端小伙伴来说,简直是"梦中情接口"!它就像是一个超级能干的"快递员",能帮我们从服务器那里把数据"搬"回来,不管是GET
请求获取数据,还是POST
请求提交数据,它都能轻松胜任。在React
、Vue
等主流前端框架
中,fetch API
的身影随处可见,已然成为前端开发
的"标配"技能。
二、痛点:传统顺序请求的"致命伤"
在没有使用并发请求之前,很多小伙伴可能会这样写代码来获取多个数据:
javascript
// 发送第一个请求,获取用户信息
fetch('https://example.com/api/user')
.then(response => response.json())
.then(data => {
console.log('用户信息:', data);
// 第一个请求成功后,再发送第二个请求,获取商品列表
return fetch('https://example.com/api/products');
})
.then(response => response.json())
.then(data => {
console.log('商品列表:', data);
// 第二个请求成功后,再发送第三个请求,获取推荐内容
return fetch('https://example.com/api/recommendations');
})
.then(response => response.json())
.then(data => {
console.log('推荐内容:', data);
})
.catch(error => {
console.error('请求出错:', error);
});
这种写法虽然能实现需求,但问题非常明显!每个请求都要等上一个请求完成后才能发送,假如第一个请求因为网络延迟等原因卡住了,后面的请求都得"干等着",页面加载速度直接"拉胯",用户体验也会变得极差。而且,这样的代码看起来十分冗长,维护起来也很麻烦。这就是传统顺序请求的"致命伤",也是我们迫切需要并发请求
的原因!
三、解决方案:fetch并发请求闪亮登场
fetch
实现并发请求的核心思路其实很简单,就是同时发送多个请求,然后等所有请求都完成后,再统一处理结果。这就好比我们点外卖,一次点了三家店的美食,不用等第一家送来了再点第二家,而是同时下单,等三家都做好送过来后再一起享受。在JavaScript
中,我们可以借助Promise.all
方法来实现这个功能。Promise.all
接受一个Promise
数组作为参数,当数组中的所有Promise
都成功时,它会返回一个包含所有成功结果的数组;只要有一个Promise
失败,它就会立即返回失败的结果。下面是具体的代码实现:
javascript
// 定义一个函数,用于发送单个fetch请求并返回Promise
const fetchData = (url) => {
return fetch(url)
.then(response => {
// 检查响应状态,如果不是200-299之间的状态码,就抛出错误
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
});
};
// 定义要发送请求的URL数组
const urls = [
'https://example.com/api/user',
'https://example.com/api/products',
'https://example.com/api/recommendations'
];
// 使用Promise.all实现并发请求
Promise.all(urls.map(fetchData))
.then(data => {
// 所有请求都成功时,打印结果
console.log('所有请求结果:', data);
})
.catch(error => {
// 只要有一个请求失败,就捕获错误并打印
console.error('请求出错:', error);
});
在这段代码中:
- 首先定义了一个
fetchData
函数,这个函数接受一个url
参数,使用fetch
发送请求。在请求成功后,通过response.ok
检查响应状态,如果状态码不是200-299
之间(表示请求成功的状态码范围),就会抛出一个错误;如果状态码正常,就将响应数据解析为JSON
格式并返回。 - 然后定义了一个
urls
数组,里面存放着我们需要请求的多个接口地址。 - 最后使用
Promise.all
,并通过urls.map(fetchData)
将每个url
都调用fetchData
函数生成对应的Promise
,组成一个Promise
数组传入Promise.all
。这样就实现了同时发送多个fetch
请求,当所有请求都完成后,会在.then
回调中得到包含所有请求结果的数组;如果有任何一个请求失败,就会在.catch
回调中捕获到错误。
四、进阶:更灵活的并发请求处理------Promise.allSettled
虽然Promise.all
能满足大部分并发请求的需求,但它有一个"致命缺点":只要有一个请求失败,整个Promise.all
就会立即失败,后面还没完成的请求结果就得不到了。有时候我们希望不管请求成功还是失败,都能知道每个请求的最终状态,这时候就可以使用Promise.allSettled
方法。它同样接受一个Promise
数组作为参数,但它会等所有Promise
都" settled"(即要么成功,要么失败)后,返回一个包含每个Promise
结果对象的数组,每个对象都有status
('fulfilled' 或'rejected')和value
(成功结果)或reason
(失败原因)属性。代码如下:
javascript
// 还是使用之前定义的fetchData函数
const fetchData = (url) => {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
});
};
const urls = [
'https://example.com/api/user',
'https://example.com/api/products',
'https://example.com/api/recommendations'
];
// 使用Promise.allSettled实现并发请求
Promise.allSettled(urls.map(fetchData))
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('请求成功,结果:', result.value);
} else {
console.error('请求失败,原因:', result.reason);
}
});
});
在这段代码中,Promise.allSettled
会等所有fetch
请求都有结果后,返回一个results
数组。我们通过遍历results
数组,根据每个结果对象的status
属性来判断请求是成功还是失败,并分别进行处理。这样即使有部分请求失败,我们也能获取到其他请求的结果,对整个并发请求的情况有更全面的了解。
五、错误处理:让请求更加"稳如老狗"
在实际开发中,除了请求本身失败(比如网络错误、服务器返回错误状态码),还可能会遇到其他各种问题,比如用户在请求过程中关闭了页面,或者fetch
不被浏览器支持等情况。为了让我们的代码更加健壮,还需要对这些情况进行处理。
- 网络错误处理 :当网络出现问题,比如用户断网了,
fetch
会抛出一个TypeError
。我们可以在fetchData
函数中捕获这个错误,给用户一个友好的提示:
javascript
const fetchData = (url) => {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
if (error instanceof TypeError) {
console.error('网络错误,请检查网络连接!');
} else {
console.error('请求出错:', error);
}
// 这里可以选择将错误继续抛出,让上层调用者处理,也可以返回一个默认值
throw error;
});
};
- 浏览器兼容性处理 :虽然
fetch API
已经得到了广泛支持,但在一些老旧浏览器中可能不支持。我们可以在使用fetch
之前进行检测,如果不支持,就使用XHR
作为替代方案:
javascript
const fetchData = (url) => {
if (!window.fetch) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`HTTP error! status: ${xhr.status}`));
}
}
};
xhr.onerror = (error) => {
reject(error);
};
xhr.send();
});
}
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
if (error instanceof TypeError) {
console.error('网络错误,请检查网络连接!');
} else {
console.error('请求出错:', error);
}
throw error;
});
};
通过这些额外的错误处理,我们的fetch
并发请求代码就能应对更多复杂的情况,变得更加"稳如老狗"!
六、总结
通过本文的学习,我们了解了fetch API
的强大之处,以及如何使用它实现并发请求
和优雅地处理错误。无论是Promise.all
还是Promise.allSettled
,都为我们提供了高效处理多个请求的方式;而全面的错误处理机制,则让我们的代码在各种复杂环境下都能稳定运行。在当今竞争激烈的前端开发领域,掌握这些技能,能让我们的页面性能
大幅提升,用户体验
更加出色,也能让我们在技术浪潮中脱颖而出!赶紧把这些知识运用到实际项目中吧,相信你会收获满满!