如何强制Chrome/Firefox的JS引擎等待动态脚本加载执行完成?
问题:脚本加载顺序失控,
make_components()提前执行 背景与目标
我写了一个loader.js脚本,想要按「变量定义→组件定义→组件渲染」的顺序加载管理组件的JS文件,代码如下:
const loadScript = (FILE_URL, async = true, type = "text/javascript") => { return new Promise((resolve, reject) => { try { const scriptEle = document.createElement("script"); scriptEle.type = type; scriptEle.async = async; //scriptEle.async = false; // 我也试过这行 scriptEle.defer = true; scriptEle.src = FILE_URL; scriptEle.addEventListener("load", (ev) => { resolve({ status: true }); }); scriptEle.addEventListener("error", (ev) => { reject({ status: false, message: `Failed to load the script ${FILE_URL}` }); }); document.body.appendChild(scriptEle); } catch (error) { reject(error); } }); }; $(document).ready(function () { setTimeout(proceed, 1); // 等文档完全就绪 }); async function proceed() { await loadVars(); // 加载变量定义 await loadComponents(); // 加载组件定义 await make_components(); // 渲染组件 } function make_components() { switch (monitorType) { // monitorType应该在loadVars执行后定义(控制台日志也确认了这一点) case 1 : addMobileHeader(wfull, hfull * 0.1); addMobileHeroBlock(wfull, hfull* 0.7); break; } } function loadVars() { loadScript("variables.js").then( data => { console.log("Variables: Script loaded successfully", data); }) .catch( err => { console.error(err); }); } function loadComponents() { loadScript("components_mobile.js").then( data => { console.log("Components (mobilephone): Script loaded successfully _ mobile", data); }) .catch( err => { console.error(err); }); loadScript("components_wide.js").then( data => { console.log("Components (widescreen):Script loaded successfully _ wide", data); }) .catch( err => { console.error(err); }); }
核心问题
make_components()总是在loadVars执行完成前就被触发,导致找不到monitorType等变量。
尝试过的方法
我本来以为先添加的variables.js会先执行,后续延迟加载的文件会按顺序执行,然后make_components再启动。我知道可以用.then链式调用,但那样代码会变得难以调试。
需求
有没有办法让Chrome/Firefox的JS引擎强制等待脚本完全加载并执行?最好不用第三方库。
解决方法
问题根源
你的loadVars和loadComponents函数没有返回Promise,导致await根本起不到等待作用——await后面必须跟Promise对象,否则会直接当成已完成的Promise处理,所以proceed会直接跳到下一步,完全不等脚本加载完成。
修复步骤
- 让
loadVars返回Promise
修改loadVars,直接返回loadScript的Promise,确保await能捕获到加载状态:
function loadVars() { return loadScript("variables.js") .then(data => { console.log("Variables: Script loaded successfully", data); }) .catch(err => { console.error(err); throw err; // 抛出错误让上层await捕获,避免后续流程继续执行 }); }
- 让
loadComponents等待所有脚本加载完成loadComponents里加载了两个脚本,需要用Promise.all确保两个脚本都加载完成后再继续:
function loadComponents() { const mobilePromise = loadScript("components_mobile.js") .then(data => { console.log("Components (mobilephone): Script loaded successfully _ mobile", data); }) .catch(err => { console.error(err); throw err; }); const widePromise = loadScript("components_wide.js") .then(data => { console.log("Components (widescreen):Script loaded successfully _ wide", data); }) .catch(err => { console.error(err); throw err; }); return Promise.all([mobilePromise, widePromise]); }
- 清理脚本加载的async/defer设置
你同时设置了async和defer,会导致行为混乱:
- 如果
async=true,浏览器会异步加载脚本,加载完成后立即执行,顺序无法保证 - 要按顺序加载,应该把
async设为false,并且去掉defer(同步加载的脚本不需要defer)
修改loadScript的默认参数或调用时传参:
// 方案1:修改函数默认值 const loadScript = (FILE_URL, async = false, type = "text/javascript") => { // ... 原代码 // 移除 scriptEle.defer = true; } // 方案2:调用时指定async=false return loadScript("variables.js", false)
最终效果
修改后,proceed函数里的await会真正等待loadVars完成(变量脚本加载并执行),再等待loadComponents的两个脚本都加载完成,最后才执行make_components,此时monitorType等变量已经定义完成。
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

