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

Dynamics 365表单加载时fetch偶发全局作用域关闭错误如何解决?

错误诱因

你遇到的TypeError: Failed to execute 'fetch' on 'Window': The global scope is shutting down报错,核心原因是触发fetch的宿主全局上下文(也就是你嵌入的HTML Web Resource对应的contentWindow)已经被销毁时,fetch请求仍被发起。
在Dynamics 365表单场景下,该报错通常由以下操作触发:

  • 表单刚加载还在初始化Web Resource的过程中,用户直接关闭表单、切换到其他记录、或者切走了Web Resource所在的标签页,导致Web Resource的窗口实例被销毁
  • Web Resource加载过程异常中断,你拿到的contentWindow已经是无效对象,但后续逻辑仍在执行

现有代码问题

  1. 初始化的轮询逻辑无边界校验:你用setInterval轮询检测Web Resource内的方法是否就绪,只有找到方法后才会清除轮询,若中途Web Resource被销毁,轮询仍会执行,调用已销毁窗口的方法时就会触发异常
  2. 未关联表单生命周期:没有绑定表单卸载事件,表单关闭/切换时不会主动中断未完成的初始化逻辑和请求
  3. fetch请求无中断机制:未使用AbortController控制fetch请求,上下文销毁后未完成的请求仍在后台运行,回调时就会触发全局作用域关闭的报错

修复方案

步骤1:改造表单初始化逻辑,绑定生命周期事件

在表单侧的脚本中存储轮询ID和请求中断控制器,表单卸载时主动清除轮询、中断未完成的请求:

// 作用域限定在当前表单脚本范围内即可
let uploaderInitInterval = null;
let fetchAbortController = null;

export function setContextForFileUploader(executionContext) {
  const formContext = executionContext.getFormContext();
  // 绑定表单销毁事件
  formContext.ui.addOnDestroy(() => {
    if (uploaderInitInterval) clearInterval(uploaderInitInterval);
    if (fetchAbortController) fetchAbortController.abort();
  });

  const wrControl = formContext.getControl('WebResource_myWebResource');
  if (wrControl) {
    wrControl.getContentWindow()
      .then(contentWindow => {
        uploaderInitInterval = self.setInterval(async () => {
          // 每次轮询先校验上下文有效性
          if (!contentWindow || !contentWindow.parent || formContext.ui.isFormClosed()) {
            clearInterval(uploaderInitInterval);
            return;
          }
          if (contentWindow.setClientApiContext && contentWindow.myNS?.EmailAttachmentUploader) {
            clearInterval(uploaderInitInterval);
            fetchAbortController = new AbortController();
            contentWindow.setClientApiContext(Xrm, formContext, fetchAbortController);
            try {
              await contentWindow.myNS.EmailAttachmentUploader.DocumentLoaded();
            } catch(e) {
              // 忽略主动中断的请求错误
              if (e.name !== 'AbortError') {
                showAlertDialog(e, 300, 600);
              }
            }
          }
        }, 200);
      })
      .catch(() => {
        // 捕获getContentWindow异常,清除无效轮询
        if (uploaderInitInterval) clearInterval(uploaderInitInterval);
      });
  }
}

步骤2:改造Web Resource内的逻辑,支持请求中断

修改setClientApiContext接收中断控制器,fetch调用时绑定中断信号,发起请求前先校验上下文有效性:

let globalAbortController = null;
function setClientApiContext(xrm, formContext, abortController) {
  window.myXrm = xrm;
  window.myFormContext = formContext;
  globalAbortController = abortController;
}

修改PopulateEntityInfo的fetch逻辑:

function PopulateEntityInfo() {
  // 上下文无效或请求已被中断时直接返回
  if (!window || globalAbortController?.signal.aborted) {
    return Promise.resolve();
  }
  const query =
    'EntityDefinitions?$select=EntitySetName,LogicalName&$filter=ObjectTypeCode eq ' + (window as any).entityTypecode;
  
  return fetch(encodeURI('API_URL' + webApiQuery), {
    signal: globalAbortController.signal
  })
  .then(async (response) => {
    if (response.ok) {
      const data = await response.json();
      if (data != null) {
        (window as any).entityCollectionName = data.value[0].EntitySetName;
        (window as any).logicalName = data.value[0].LogicalName;
      }
    } else {
      showAlertDialog(
        'There was a problem fetching entity definitions.',
        300,
        600
      );
    }
  })
  .catch((error) => {
    if (error.name !== 'AbortError') {
      showAlertDialog(error, 300, 600);
    }
  });
}

可选优化

  • 替换轮询逻辑:可以直接绑定Web Resource控件的onLoad事件检测资源加载完成,替代setInterval轮询,减少无效执行
  • 延迟初始化:如果Web Resource放在非默认标签页中,可以绑定标签页的TabStateChange事件,只有用户切到对应标签页时再执行初始化逻辑,避免无效的资源调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 19:06:03