如何捕获Adblocker导致的net::ERR_BLOCKED_BY_CLIENT错误,保障JS初始化?
这个问题我之前处理过,广告拦截器触发的net::ERR_BLOCKED_BY_CLIENT错误确实会让jQuery的$.getScript和$.when组合陷入困境——默认情况下,这种网络级别的拦截不会被$.getScript的错误回调捕获,导致对应的Deferred一直处于pending状态,最终整个$.when的.done永远不会触发。
问题根源
$.getScript本质是$.ajax的简写,但它的错误处理逻辑对网络拦截类的错误支持不好。当请求被广告拦截器阻断时,浏览器不会返回标准的HTTP错误状态码,jQuery无法识别这种失败,也就不会触发Deferred的reject,导致$.when因为有一个pending的Deferred而无法进入done阶段。
解决方案:封装带可靠错误处理的脚本加载函数
我们可以自己封装一个safeGetScript函数,用原生$.ajax实现,确保任何加载失败(包括拦截)都能正确触发Deferred的状态变更,甚至可以让失败的请求也resolve,保证整体流程能继续执行。
步骤1:封装安全的脚本加载函数
function safeGetScript(url) { return $.ajax({ url: url, dataType: "script", cache: true // 可选,开启脚本缓存提升性能,根据需求调整 }).fail(function(xhr, status, error) { // 捕获所有加载失败情况,包括广告拦截器的阻断 console.warn(`脚本加载失败: ${url}, 错误信息: ${error}`); // 关键:即使加载失败,也返回一个resolve的Deferred,保证整体流程不中断 return $.Deferred().resolve(); }); }
步骤2:替换原有代码中的$.getScript
把原来的$.getScript都换成我们封装的safeGetScript,这样每个脚本请求无论成功还是失败,都会让对应的Deferred进入resolved状态,$.when就能顺利触发.done回调:
$.when( safeGetScript("http://example.com/script1.js"), safeGetScript("http://example.com/script2.js"), safeGetScript("http://example.com/ads-script.js"), $.Deferred(function(deferred) { $(deferred.resolve); }) ).done(function() { console.log('所有脚本请求已处理完成(含失败的广告脚本)'); initialize(); });
进阶:区分成功/失败的脚本(可选)
如果你需要知道哪些脚本成功加载、哪些被拦截,可以修改safeGetScript返回状态标记,在.done中查看结果:
function safeGetScript(url) { return $.ajax({ url: url, dataType: "script", cache: true }).then( // 成功时返回true () => ({ url, success: true }), // 失败时返回false和错误信息 (xhr, status, error) => { console.warn(`脚本加载失败: ${url}, 错误信息: ${error}`); return { url, success: false, error }; } ); } // 批量处理脚本请求 const scriptUrls = [ "http://example.com/script1.js", "http://example.com/script2.js", "http://example.com/ads-script.js" ]; const deferreds = scriptUrls.map(url => safeGetScript(url)); // 加上DOM ready的Deferred deferreds.push($.Deferred(deferred => $(deferred.resolve))); $.when.apply($, deferreds).done(function(...results) { console.log('所有脚本请求处理完成:', results); // 可以在这里过滤成功/失败的脚本 const failedScripts = results.filter(res => res?.success === false); if (failedScripts.length > 0) { console.log('被拦截或加载失败的脚本:', failedScripts.map(res => res.url)); } initialize(); });
为什么原来的fail/error回调无效?
因为$.getScript的错误回调只处理HTTP级别的错误(比如404、500),而广告拦截器的net::ERR_BLOCKED_BY_CLIENT属于浏览器层面的网络拦截,不会触发jQuery默认的错误处理逻辑。改用$.ajax后,我们能捕获到更底层的错误事件,确保Deferred状态正确更新。
内容的提问来源于stack exchange,提问作者Billy

