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

快速重复点击按钮时,为何console.log异步执行顺序异常?

快速重复点击"CLICK ME"按钮时,为何console.log(=${innerCount}.2);可能先于console.log(=${innerCount}.1);执行?

问题核心原因

出现执行顺序颠倒的情况,本质是lodash throttle的节流逻辑与异步函数的await行为共同作用的结果,结合两层节流的时间差(50ms vs 100ms),具体拆解如下:

  1. lodash throttle的默认行为
    lodash的throttle默认配置为{leading: true, trailing: false}:

    • 节流周期的首次调用会立即执行原函数;
    • 节流周期内的后续调用会被直接忽略,不会执行原函数,且返回undefined。
  2. 异步函数的await处理
    await关键字如果接收到非Promise类型的值(比如undefined),会自动将其转换为已resolved的Promise,代码会立即继续执行后续逻辑。

  3. 快速点击时的执行流程
    假设快速点击触发两次handleClick执行:

    • 第一次执行(innerCount=1):
      handleClick调用throttledFetchData(1),因是节流周期内首次调用,throttledFetchData的原异步函数立即执行,进入await callApi()等待接口响应,此时handleClick(1)处于等待状态,两个console.log都未执行。
    • 第二次执行(innerCount=2,间隔50ms):
      handleClick调用throttledFetchData(2),此时距离throttledFetchData首次调用还未到100ms节流周期,lodash忽略原函数执行,直接返回undefined。
      由于await undefined会立即resolve,handleClick(2)跳过等待,直接执行console.log('===2.2')。
    • 后续:
      当第一次callApi()响应完成后,才会执行console.log('===1.1'),最后执行console.log('===1.2')。

    这就导致了===2.2先于===1.1出现在控制台的现象。

补充说明

  • 两层throttle是失误操作,调试时触发了该执行顺序问题;
  • 问题仅在快速重复点击时才会复现,因为只有高频触发才会导致handleClick在throttledFetchData的节流周期内重复调用。

现象展示

控制台内容
复现动图

代码示例

let count = 0;
const App = () => {
  const throttledFetchData = throttle(async (innerCount) => {
    const result = await callApi();
    console.log(`===${innerCount}.1`);
    return result;
  }, 100);

  const handleClick = throttle(async (e) => {
    count++;
    const innerCount = count;
    const result = await throttledFetchData(innerCount);
    console.log(`===${innerCount}.2`);
  }, 50);

  return (
    <div className='container' onClick={handleClick}>
      CLICK ME
    </div>
  );
}

注:代码中throttle来自lodash

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 22:05:23