竞态条件Race condition及如何避免的三种方案详解

作者:小括号

时间:2026-10-31

浏览:379

本文深入解析前端开发中常见的竞态条件问题,通过UI交互案例说明其成因,并详细阐述三种主流解决方案:操作拦截、丢弃旧响应及请求取消。每种方案均配有核心代码逻辑,帮助开发者有效避免数据展示不同步的错误。

文章目录

什么是竞态条件?

当程序逻辑依赖于特定的响应顺序,而实际执行顺序无法得到保证时,便可能引发非预期的结果,这种现象被称为竞态条件。

以下以用户界面交互为例进行说明:

什么是竞态条件?

如上图所示,当用户分别点击“科技”和“生活”分类标签时,系统预期展示对应的分类数据。

假设发生如下场景:

  • 首先点击“科技”按钮(界面高亮显示“科技”),该请求的数据将在2秒后返回;
  • 随后立即点击“生活”按钮(界面高亮切换为“生活”),该请求的数据在1秒后返回。最终,界面会在1秒后先显示“生活”数据,而在2秒后更新为“科技”数据。此时,尽管按钮高亮状态停留在“生活”,但展示区域却显示了“科技”数据。这种高亮状态与展示内容不同步的现象,正是由竞态条件引发的典型问题。

如何避免?

方案1:每次操作完成之前,阻止新的操作

该方案应用较为广泛。其核心思路是在发起请求后添加一个loading遮罩层,在当前请求响应返回之前,通过遮罩层拦截后续的用户操作,从而从源头上杜绝竞态问题的发生。

方案2:每次发送请求时,丢掉上一个请求的响应

此方案的逻辑是:在最后一次响应完成前,若用户发起新请求,则直接丢弃未完成的旧请求。其结果是系统仅对最新的请求做出响应,从而避免旧的请求响应数据错误地展示在当前高亮的视图下。

function getResolveWhenLast() {
    let globalId = 0;
    return (pro) => {
    return new Promise((resolve, reject) => {
      const id = ++globalId;
      pro
        .then((res) => {
          if (id === globalId) {
              resolve(res);
          }
        })
        .catch(err => {
          if (id === globalId) {
              reject(err);
          }
        })
    })
    }
}
const resolveWhenLast = getResolveWhenLast()
// 使用resolveWhenLast包住你的请求,就可以解决竞态问题
resolveWhenLast(api.getPosts()).then(res => {
    // ...
})

方案3:每次发送请求时,取消掉上一次的请求

通过对Promise进行封装,增加cancel能力。每次发起新请求时,主动取消上一次的请求,同样实现了只处理最新一次请求响应的目标,进而避免竞态条件的发生。

// 给Promise添加取消请求的能力
function createImpretivePromise(pro) {
  let resolve = null;
  let reject = null;
  const warppedPromise = new Promise((_resolve, _reject) => {
      resolve = _resolve;
      reject = _reject;
  })
   pro
    .then((res) => {
      resolve && resolve(res);
    })
    .catch((err) => {
      reject && reject(err);
    });
  // 可以切断代理
  const cancel = () => {
    resolve = null;
    reject = null;
  }
  return {
    promise: warppedPromise,
    cancel
  }
}
const getResolveWhenLast = () => {
  let globalCancel = null;
  return (pro) => {
    const { promise, cancel } = createImperativePromise(pro);
    globalCancel && globalCancel();
    globalCancel = cancel;
    return promise;
  };
};
const resolveWhenLast = getResolveWhenLast()
// 使用resolveWhenLast包住你的请求,就可以解决竞态问题
resolveWhenLast(api.getPosts()).then((res) => {
    // ...
});

以上就是竞态条件Race condition及如何避免的三种方案详解的详细内容,更多关于竞态条件 Race condition的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章