如何避免从defer脚本标签调用函数时的竞态条件与未定义错误?
解决defer脚本调用函数的竞态问题
问题原因分析
你遇到的偶尔报错情况,大概率不是defer属性失效,而是以下两种常见情况:
- script1路径错误或加载失败:如果
src="script1"缺少后缀(比如.js),或者网络波动导致脚本加载超时、失败,自然无法定义myFunction。 - 极端场景下的执行顺序偏差:虽然
defer脚本会在DOMContentLoaded事件前执行,window.onload会等所有资源加载完成才触发,但个别浏览器在缓存异常、资源阻塞时,可能出现脚本执行滞后于window.onload的极端情况。
可靠解决方案
方案1:先校验函数存在性再调用
在window.onload回调里先判断myFunction是否存在,避免报错同时排查问题:
<!DOCTYPE html> <html> <head> <title>Home</title> <script defer src="script1.js"></script> </head> <body> <script type="text/javascript"> window.onload = function () { if (typeof myFunction === 'function') { myFunction(); } else { console.error('myFunction未定义,请检查script1是否加载成功'); } } </script> </body> </html>
方案2:监听脚本加载完成事件
直接给script1绑定onload事件,确保脚本加载并执行后再调用函数;如果需要等页面资源全部加载完成,就结合window.onload:
<!DOCTYPE html> <html> <head> <title>Home</title> <script defer src="script1.js" onload="setupMyFunction()"></script> </head> <body> <script type="text/javascript"> function setupMyFunction() { window.onload = function () { myFunction(); } } </script> </body> </html>
方案3:用Promise封装脚本加载(最稳定)
通过Promise明确等待脚本加载成功,再执行后续逻辑,彻底避免竞态:
<!DOCTYPE html> <html> <head> <title>Home</title> </head> <body> <script type="text/javascript"> function loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.defer = true; script.onload = () => resolve(); script.onerror = () => reject(new Error(`加载脚本${src}失败`)); document.head.appendChild(script); }); } // 先加载script1,成功后再绑定window.onload调用函数 loadScript('script1.js') .then(() => { window.onload = function () { myFunction(); }; }) .catch(err => console.error(err)); </script> </body> </html>
额外排查步骤
- 打开浏览器开发者工具的「网络」面板,查看
script1的加载状态,确认是否有404或加载失败的情况。 - 检查
script1内部是否有异步逻辑导致myFunction定义延迟(比如在回调里定义函数),这种情况需要调整script1的代码,确保函数在脚本执行时同步定义。
内容的提问来源于stack exchange,提问作者Resident Zero9999
相关产品推荐
相关产品推荐

