You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现带3次重试的动态JS脚本加载功能时appendChild报错如何解决

问题原因

原实现存在三个核心问题,导致异常无法捕获、执行逻辑不符合预期:

  • 执行顺序错位:document.head.appendChild(element) 执行在Promise初始化之前,脚本开始加载时,内部存储的resolve/reject还是初始的空函数,若加载回调触发早于Promise初始化,加载结果会直接丢失。
  • 递归链路断裂:外层调用方拿到的永远是第一次函数调用返回的Promise,后续递归调用生成的新Promise没有和外层Promise关联;同时递归时传入的共享状态对象会被每次新建的Promise覆盖resolve/reject回调,最外层的Promise始终无法正常决议,递归内部抛出的DOM操作异常、加载错误都不会进入外层Promise的捕获链。
  • DOM操作无防护:removeChild 没有提前判断元素是否真的存在于DOM树中,极端场景下该操作本身就会抛出未捕获异常;重试时新创建的script元素append操作如果出现异常,也因为处于异步回调中没有被Promise包裹,无法被外层捕获。
修复方案

用Promise链透传重试结果的写法替代共享状态对象+递归的逻辑,所有DOM操作都包裹在Promise执行上下文里,所有异常统一归到Promise的reject逻辑,重试计数独立维护,从根源避免链路断裂问题。

async function getScript(url: string, maxRetry = 3): Promise<boolean> {
  let retryCount = 0;

  const load = (): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      const element = window.document.createElement('script');
      element.src = url;
      element.type = 'text/javascript';
      element.async = true;

      element.onload = () => {
        resolve(true);
      };

      element.onerror = () => {
        // 安全移除加载失败的脚本元素
        if (element.parentNode) {
          element.parentNode.removeChild(element);
        }
        // 未达最大重试次数则递归重试,结果直接透传到当前Promise
        if (retryCount < maxRetry) {
          retryCount++;
          console.log(`retry load script, current retry count: ${retryCount}`);
          load().then(resolve, reject);
          return;
        }
        reject(new Error(`Dynamic Script Error: ${url}`));
      };

      // append操作加异常防护,错误直接进入reject逻辑
      try {
        document.head.appendChild(element);
      } catch (err) {
        reject(err);
      }
    });
  };

  return load();
}

修复点说明

  • 去掉了共享状态对象,每次加载都返回独立的Promise,重试时直接把新Promise的结果透传给外层,整个调用链完全连通,加载成功、重试成功、加载失败、DOM操作异常所有场景的结果都能被外层正常捕获。
  • DOM操作全加防护:移除脚本前先判断父节点存在,append操作包裹try/catch,从根源避免DOM操作抛出未捕获异常。
  • 重试计数独立维护,不会因为递归传参混乱导致计数错误,最大重试次数支持自定义。
  • 调用方式和原逻辑完全兼容,直接用await getScript('target.js')即可,成功返回true,超过最大重试次数或出现操作异常时会正常抛出错误,可直接通过try/catch捕获。

内容的提问来源于stack exchange,提问作者duocai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:36:52