onerror不监听fetch或XHR请求,仅适用于img、script等元素的资源加载失败;异步循环请求需用Promise.allSettled单独处理每个fetch的fulfilled/rejected状态,并检查response.ok和status。

onerror 在异步循环请求中根本不会触发
这是最常被误解的一点:onerror 属性只对 HTML 元素(如 <img>、<script></script>、<link>)的资源加载失败有效,它**完全不监听 fetch 或 XMLHttpRequest 发起的异步请求**。你在 for 循环里写 fetch(...).catch(...),再给它加 onerror="..." 是无效的——浏览器会直接忽略。
循环发 fetch 时怎么捕获单个请求失败
每个 fetch 调用是独立 Promise,必须单独处理错误,不能靠全局或元素级事件兜底。常见错误是把所有请求塞进 Promise.all 然后只写一个 .catch,结果一个失败就全崩。
- 用
Promise.allSettled替代Promise.all:它返回每个请求的{ status: 'fulfilled' | 'rejected', value | reason },可逐个判断 - 避免在循环内直接
await fetch():阻塞式串行太慢;改用map+Promise.allSettled并行但可控 -
fetch成功返回不代表业务成功:必须检查response.ok和response.status,404/500 不会进.catch - 网络中断、CORS 拒绝、DNS 失败等才会真正 reject,这类错误才进
.catch
示例:
const urls = ['/api/item/1', '/api/item/2', '/api/item/3'];
const results = await Promise.allSettled(
urls.map(url =>
fetch(url)
.then(res => res.ok ? res.json() : Promise.reject(new Error(`HTTP ${res.status}`)))
.catch(err => ({ error: err.message }))
)
);
results.forEach((r, i) => {
if (r.status === 'rejected') console.error(`第 ${i+1} 个请求失败:`, r.reason);
});
为什么给
写 onerror 对 fetch 循环没用
有人试图在循环里动态创建 <img src="..." onerror="handleFail()"> 来“借力”错误处理,这看似取巧,实则危险:
-
onerror只响应该<img>的图片加载,和你的 fetch 请求完全无关 - 若你用
src指向一个 API 接口(比如src="/api/avatar?id=123"),那失败确实会触发onerror,但此时你已混用语义:这不是图片资源,而是数据接口,违反 HTML 规范,且服务端可能返回非图片内容导致渲染异常 - 多个
<img>同时触发onerror回调,容易因闭包或 this 指向错乱导致状态错配 - CSP 策略可能禁止动态插入的
<img>加载外域资源,onerror还是不触发——你以为有兜底,其实没有
真正需要的是结构化错误隔离与重试控制
异步循环的本质是并发任务管理,不是 DOM 事件代理。重点不在“怎么捕获”,而在“失败后怎么不干扰其他请求、是否重试、是否降级”。
- 每个请求应带唯一标识(如
id或索引),错误日志里必须包含它,否则排查时无法定位是哪个 URL 崩了 - 重试逻辑不要写在
.catch里递归调用自身,容易栈溢出;用带计数的 while 循环 +await new Promise(r => setTimeout(r, 1000)) - 对关键请求可设 fallback 接口(如 CDN 失效切回主站),但要避免 fallback 接口也挂掉后无限降级
- 用户侧提示别堆弹窗:统一收集失败项,最后聚合展示“共 3 项加载失败,点击查看详情”
最易被忽略的一点:循环发起的请求如果依赖上一个响应结果(比如分页游标),就不能用 allSettled,而必须用串行 + 显式错误分支终止,否则会拿到脏数据或空响应。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











