如何映射参数为延迟执行Promise 实现任意层级脚本按序并行加载
脚本分层并行加载实现方案
核心问题原因
你之前的实现无法按预期分层加载,主要是两个问题:
- Promise热执行特性:只要调用
loadScript()返回Promise实例,对应的脚本加载逻辑就会立刻启动,不会等你调用await或.then才执行。原来的抽象代码在遍历层级映射时就调用了所有loadScript,等于所有层级的脚本同时开始加载,自然无法实现分层等待的效果。 - 原有固定层级代码的隐藏bug:
.then(Promise.all(second))写法错误,then必须接收回调函数作为参数,直接传入Promise.all(second)会导致该Promise在同步阶段就运行,两层脚本依然会同时加载,正确写法应为.then(() => Promise.all(second))。
可直接使用的实现(支持任意层级、兼容序号跳号)
这个实现把层级收集、排序、加载逻辑整合,不需要额外的队列预存逻辑,走到对应层级才会触发该层脚本加载,天然满足「同层并行、层间串行」的要求:
function loadScript (script) { return new Promise(function(resolve, reject) { script.setAttribute("src", script.getAttribute("data-src")); script.onload = function() { resolve(script.src); }; script.onerror = function() { reject(script.src); } }); } async function loadScripts() { // 收集所有标记了懒加载的脚本 const allLazyScripts = document.querySelectorAll("script[data-type='lazy'][data-order]"); // 按data-order值分组 const hierarchy = new Map(); allLazyScripts.forEach(script => { const order = Number(script.getAttribute("data-order")); if (!hierarchy.has(order)) hierarchy.set(order, []); hierarchy.get(order).push(script); }); // 按层级序号从小到大排序,兼容跳号、非0/1起始的场景 const sortedLevels = [...hierarchy.keys()].sort((a, b) => a - b); // 逐层加载:当前层全部加载完成后才进入下一层 for (const level of sortedLevels) { const currentLevelScripts = hierarchy.get(level); // 仅当遍历到当前层级时,才调用loadScript触发该层脚本加载 const currentLevelTasks = currentLevelScripts.map(script => loadScript(script)); await Promise.all(currentLevelTasks); } console.log('all scripts loaded'); } // 执行加载,统一捕获加载失败 loadScripts().catch(function(failedScriptSrc) { console.log(failedScriptSrc + ' failed to load.'); });
延迟队列写法(如果需要提前定义加载任务)
如果你需要提前把各层加载逻辑存为队列、在需要的时候再启动执行,只需要把每层的加载逻辑包装为返回Promise的函数即可——只有调用函数时才会真正执行loadScript触发加载,不会提前启动:
// 收集、分组、排序层级逻辑和上面一致 const sortedLevels = [...hierarchy.keys()].sort((a, b) => a - b); // 提前构建任务队列,此时不会触发任何脚本加载 const loadTaskQueue = sortedLevels.map(level => { const scripts = hierarchy.get(level); // 返回函数而非直接返回Promise,实现延迟执行 return () => { const levelTasks = scripts.map(script => loadScript(script)); return Promise.all(levelTasks); } }); // 统一的层级遍历执行函数 async function walkHierarchy(taskQueue) { for (const runLevelTask of taskQueue) { await runLevelTask(); } } // 需要启动加载时调用即可 walkHierarchy(loadTaskQueue) .then(() => console.log('all scripts loaded')) .catch(failedSrc => console.log(failedSrc + ' failed to load.'));
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

