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

多Adobe Target实验同页面复用sessionStorage实现单次API调用异常排查

问题根因

两个并行加载的实验脚本触发重复API请求,本质是两个时序问题叠加出来的:

  • 异步逻辑时序错位:两个实验script是同步加载执行的,但API请求是异步操作。第一个脚本运行时,会立刻查sessionStorage发现没缓存,随即触发异步API请求——这时候脚本的同步逻辑就走完了,根本不会等API返回、把结果写进sessionStorage。浏览器紧接着执行第二个实验脚本时,第一个脚本的API请求还在等待网络响应,sessionStorage里自然没有sparksSSO的内容,第二个脚本就会判定无缓存、重新发起请求。
  • 全局变量覆盖冲突:两个脚本都用var sparksSSO声明全局变量,第二个脚本执行时会直接把第一个脚本的全局变量值覆盖掉,哪怕后续API返回、写入sessionStorage,两个脚本也只会保留自己同步执行阶段拿到的空对象,根本感知不到后续写入的缓存。

你之前逐标签页打开测试完全正常,是因为每次开新标签页时,sessionStorage已经存好了上一次请求完成的结果,不存在并行运行、等待异步请求的时间差,自然能正常命中缓存。

调整方案

要实现同一页面只发一次请求、所有实验复用缓存的目标,核心是把“只查询最终缓存值”的逻辑,改成“能识别正在进行的请求状态”,消除异步时序带来的竞态问题:

  • 抽离一套统一的公共取数逻辑,所有实验不要单独实现API请求、sessionStorage读写的代码,全部统一调用这套方法
  • 取数时按三级优先级判断,从根源杜绝重复请求:
    1. 先查sessionStorage里有没有已经存好的sparksSSO结果,有的话直接返回数据
    2. 如果没有写入完成的缓存,再检查全局对象(直接挂在window上即可)有没有正在等待返回的API请求Promise,有的话直接复用这个Promise等待结果,绝不重复发请求
    3. 如果既没有存好的缓存,也没有正在运行的请求,就触发第一次API请求,并且立刻把这个请求的Promise挂到全局对象上,等请求成功返回后,再把序列化好的结果写进sessionStorage
  • 移除实验脚本里的裸全局变量声明(比如直接写var sparksSSO = ...),所有实验需要的SSO数据,都从统一取数方法的回调里获取,避免两个脚本的变量互相覆盖。
可直接复用的参考代码

公共取数方法(所有实验共用,仅需实现一次):

function getSparksSSOData() {
  // 优先读取已完成的缓存
  const cachedResult = sessionStorage.getItem('sparksSSO');
  if (cachedResult) return Promise.resolve(JSON.parse(cachedResult));

  // 复用正在进行的请求,避免重复发起
  if (window.__sparksSSORequest) return window.__sparksSSORequest;

  // 首次发起请求,立刻把Promise挂载到全局
  window.__sparksSSORequest = fetch('/你的SSO数据接口地址')
    .then(res => res.json())
    .then(data => {
      // 拿到结果立刻写入sessionStorage
      sessionStorage.setItem('sparksSSO', JSON.stringify(data));
      return data;
    })
    .catch(error => {
      // 请求失败时清空全局挂载的Promise,允许后续重试
      window.__sparksSSORequest = null;
      throw error;
    });

  return window.__sparksSSORequest;
}

两个实验的业务逻辑直接复用这个方法即可,不需要改动原有DOM操作逻辑:

// EXP33实验逻辑
getSparksSSOData().then(sparksSSO => {
  const sparksOfferCount = sparksSSO.totalOffers >=0 ? sparksSSO.totalOffers : '';
  const sparksAllOffers = sparksSSO.allOffers;
  // 其余原有实验DOM交互逻辑
});

// EXP78实验逻辑
getSparksSSOData().then(sparksSSO => {
  const sparksOfferCount = sparksSSO.totalOffers >=0 ? sparksSSO.totalOffers : '';
  const sparksAllOffers = sparksSSO.allOffers;
  // 其余原有实验DOM交互逻辑
});

不管两个实验脚本谁先执行,第一次触发的请求会把Promise挂载到全局,后执行的脚本会直接复用这个等待中的Promise,不会重复发起网络请求,等数据返回后两个实验都会拿到同一份缓存数据执行逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 08:54:03