onerror仅捕获加载失败(如404、跨域拒绝),不响应超时;真正超时降级需用fetch+AbortController或Image+setTimeout手动实现,成功后赋值src,失败则降级,并注意blob回收与fallback防循环。

图片加载失败时如何触发降级逻辑
原生 <img> 标签没有内置超时机制,只能靠 onerror 捕获加载失败(如 404、跨域拒绝、网络中断),但这个事件不区分“超时”和“真失败”。所以真正的“超时降级”必须手动控制:用 fetch 或 XMLHttpRequest 预检图片 URL,设置 timeout,成功后再赋值给 src;失败则走降级路径。
常见错误是直接监听 onerror 就替换为备用图——这会把短暂网络抖动、CDN 缓存未就绪等临时问题误判为永久失败,导致不该降级的时候降了。
-
onerror只能捕获资源加载后返回非 2xx/3xx 或解析失败(如损坏的 JPEG),不触发超时 - 浏览器对图片请求不支持
AbortController.timeout(Chrome 123+ 才开始实验性支持) - 降级图本身也应加
onerror并指向最小 fallback(如 base64 占位符或空src),避免二次失败无限循环
用 fetch + AbortController 实现可控超时加载
这是目前最可靠的方式:用 fetch 请求图片 URL,设置 AbortController 超时,响应成功后转成 blob URL 赋给 <img> 的 src。注意不能直接用 fetch 返回的 URL,因为跨域图片需 blob 中转才能避免 CORS 限制显示。
function loadImgWithTimeout(src, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(src, { signal: controller.signal })
.then(res => {
clearTimeout(timeoutId);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.blob();
})
.then(blob => URL.createObjectURL(blob))
.catch(err => {
console.warn('图片加载超时或失败:', src, err);
return null; // 交给上层决定降级行为
});
}
// 使用示例
const img = document.querySelector('#my-img');
loadImgWithTimeout('https://example.com/photo.jpg', 3000)
.then(url => {
if (url) img.src = url;
else img.src = '/fallback.png'; // 降级图
});
- 务必调用
clearTimeout,否则成功后仍可能触发 abort 导致后续 Promise reject - blob URL 需要手动回收(
URL.revokeObjectURL)以防内存泄漏,尤其在频繁切换图片时 - 若目标图片本就同源,可跳过 blob 转换,直接赋
src;但多数 CDN 场景需 blob 中转
兼容性差时的降级兜底方案
IE 和部分旧版 Safari 不支持 AbortController,此时只能用 setTimeout + Image 对象模拟超时:创建 new Image(),监听 onload/onerror,同时启动定时器,超时未触发任一回调即判定失败。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
立即学习“前端免费学习笔记(深入)”;
function loadImgFallback(src, timeout = 5000) {
return new Promise((resolve, reject) => {
const img = new Image();
const timer = setTimeout(() => {
img.onload = img.onerror = null;
reject(new Error('timeout'));
}, timeout);
img.onload = () => {
clearTimeout(timer);
resolve(img.src);
};
img.onerror = () => {
clearTimeout(timer);
reject(new Error('load error'));
};
img.src = src;
});
}
-
Image对象的onload触发时机与<img>标签一致,但更可控,适合封装 - 该方法无法区分 DNS 失败、TCP 连接超时、SSL 握手失败等底层错误,统一归为“timeout”,但比纯
onerror更接近真实超时语义 - 注意不要重复设置
img.src,否则可能触发两次请求
实际部署中容易忽略的细节
超时值不是越短越好。3G 网络下首屏图片加载常需 2–4 秒,设 1s 超时会导致大量误降级;CDN 回源慢的场景甚至需要 8s+。建议按监控数据设定分位值(如 p95 加载耗时 ×1.5),并区分首屏/懒加载图片。
- 服务端返回 200 但 body 为空(如 Nginx 配置错误),
fetch会 resolve,但后续blob()抛错,需在.then(blob => ...)后加catch捕获 - 降级图也要做缓存优化:使用小尺寸、WebP 格式,避免降级后反而拖慢渲染
- 若页面有多个图片需超时控制,避免每个都新建
AbortController,可复用或用工厂函数管理生命周期
真正难的不是写超时逻辑,而是定义“什么是可接受的等待时间”——它取决于你的用户网络分布、CDN 质量、图片重要性,这些没法靠代码自动判断。


















