fetch请求超时未显示指定Sweetalert2,触发通用错误弹窗问题
请求超时后错误弹窗异常的修复方案
问题现象
- 代码设置20秒请求超时,超时后通过
controller.abort()取消请求,预期展示“服务器超时”的Sweetalert2弹窗,但实际触发了catch块中的通用错误弹窗 - 控制台输出错误:
DOMException: The user aborted a request
原代码
const envSoli = async () => { try { const controller = new AbortController(); const signal = controller.signal; const timeId = setTimeout(() => { Swal.fire({ //error timeout //custom function translate text: translate("text1"), icon: "info", buttonsStyling: false, confirmButtonText: translate("confirmbtn"), allowOutsideClick: false, allowEscapeKey: false, customClass: { confirmButton: "btn btn-info", }, //refresh on ok click button }).then(function () { location.reload(); }); //controller.abort(), the idea is abort a request //after show the sweetalert controller.abort(); }, 20 * 1000); // 20 sec let peticion = await fetch("data.php", { method: "POST", body: "ajax=1&do=check&lista=" + encodeURIComponent(leray[chenille]), headers: { "Content-type": "application/x-www-form-urlencoded" }, cache: "no-cache", signal: signal, }); clearTimeout(timeId); let oreen = await peticion.json(); switch (oreen.enviando) { case -1: chenille++; document.getElementById("div1").append(oreen.cat + "<br />"); updateProgress(chenille, leray.length); tvmit_wrongUp(); break; case 1: chenille++; document.getElementById("div1").append(oreen.dog + "<br />"); updateProgress(chenille, leray.length); tvmit_wrongUp(); break; case 2: chenille++; document.getElementById("div2").append(oreen.sky + "<br />"); nieva++; updateProgress(chenille, leray.length); tvmit_dieUp(); break; case 3: chenille++; document.getElementById("div3").append(oreen.water + "<br />"); tvmit_liveUp(); updateProgress(chenille, leray.length); break; } OKTY(leray, chenille, aarsh, nieva); return true; } catch (error) { console.log(error); Swal.fire({ text: translate("text2"), icon: "question", buttonsStyling: false, confirmButtonText: translate("confirmbtn"), allowOutsideClick: false, allowEscapeKey: false, customClass: { confirmButton: "btn btn-primary", }, //refresh again on button click }).then(function () { location.reload(); }); } }; envSoli();
问题根源
调用controller.abort()会让fetch请求抛出AbortError(属于DOMException的子类),这个错误会被try/catch的catch块捕获,导致通用错误弹窗被触发,覆盖了之前的超时提示弹窗。
修复步骤
- 区分错误类型:在catch块中判断错误是否为
AbortError,如果是则跳过通用错误弹窗的逻辑——因为超时提示已经展示 - 调整超时逻辑顺序:先调用
controller.abort()取消请求,再显示超时弹窗,逻辑更合理
修改后的代码
const envSoli = async () => { try { const controller = new AbortController(); const signal = controller.signal; const timeId = setTimeout(() => { // 先取消请求,再展示超时提示 controller.abort(); Swal.fire({ text: translate("text1"), icon: "info", buttonsStyling: false, confirmButtonText: translate("confirmbtn"), allowOutsideClick: false, allowEscapeKey: false, customClass: { confirmButton: "btn btn-info", }, }).then(function () { location.reload(); }); }, 20 * 1000); // 20秒超时 let peticion = await fetch("data.php", { method: "POST", body: "ajax=1&do=check&lista=" + encodeURIComponent(leray[chenille]), headers: { "Content-type": "application/x-www-form-urlencoded" }, cache: "no-cache", signal: signal, }); clearTimeout(timeId); let oreen = await peticion.json(); switch (oreen.enviando) { case -1: chenille++; document.getElementById("div1").append(oreen.cat + "<br />"); updateProgress(chenille, leray.length); tvmit_wrongUp(); break; case 1: chenille++; document.getElementById("div1").append(oreen.dog + "<br />"); updateProgress(chenille, leray.length); tvmit_wrongUp(); break; case 2: chenille++; document.getElementById("div2").append(oreen.sky + "<br />"); nieva++; updateProgress(chenille, leray.length); tvmit_dieUp(); break; case 3: chenille++; document.getElementById("div3").append(oreen.water + "<br />"); tvmit_liveUp(); updateProgress(chenille, leray.length); break; } OKTY(leray, chenille, aarsh, nieva); return true; } catch (error) { console.log(error); // 仅当错误不是请求主动取消时,才展示通用错误弹窗 if (error.name !== 'AbortError') { Swal.fire({ text: translate("text2"), icon: "question", buttonsStyling: false, confirmButtonText: translate("confirmbtn"), allowOutsideClick: false, allowEscapeKey: false, customClass: { confirmButton: "btn btn-primary", }, }).then(function () { location.reload(); }); } } }; envSoli();
关键修改点
- 超时回调中先执行
controller.abort(),再弹出超时提示 - catch块中增加
error.name !== 'AbortError'的判断,过滤掉主动取消请求的错误,避免触发通用弹窗
内容的提问来源于stack exchange,提问作者SadDeepLoper
相关产品推荐
相关产品推荐

