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

如何实现函数调用排队,让每次调用在前一个执行完成后触发

问题:实现点击请求排队的slideUp动画

我写了一个slideUp函数,点击按钮会触发它,这个函数会调用返回Promise的异步函数removeSlideUp。但多次点击按钮时,removeSlideUp会被立即触发多次,我希望所有点击请求进入队列,等上一次的transitionend事件完成后,再按顺序逐个执行removeSlideUp。我是JavaScript新手,不知道问题出在哪。

原代码(含jQuery,需改为纯JS)

async function slideUp() {
  await removeSlideUp();
}

function removeSlideUp() {
  return new Promise(function (resolve, reject) {
    //console.log(smartAlert.classList.contains("slideup"));
    if (smartAlert.classList.contains("slideup") === false) {
      smartAlert.classList.add("slideup");
      smartAlert.addEventListener(
        "transitionend",
        function () {
          console.log("completed");
          smartAlert.classList.remove("slideup");
          resolve();
        },
        { once: true }
      );
    } else {
      smartAlert.classList.remove("slideup");
      resolve();
    }
  });
}

// 原代码用了jQuery,需替换为纯JS
// $('button[name="btn_show"]').click(function () {
//   slideUp();
// });

问题根源

每次点击按钮都会独立调用slideUp(),虽然slideUp里用了await,但这些调用彼此独立,没有形成顺序执行的队列。前一个动画还没完成,下一个点击就已经触发了新的removeSlideUp调用,导致动画混乱。

解决方案:维护Promise队列

我们需要一个全局的Promise变量来保存当前的执行队列,每次点击都把新的任务追加到队列末尾,确保上一个任务完成后再执行下一个。

修正后的完整代码

// 获取DOM元素
const smartAlert = document.getElementById("smart-alert");
const buttonShow = document.getElementById("btn_show");

// 维护执行队列的Promise,初始为已完成状态
let queuePromise = Promise.resolve();

async function slideUp() {
  await removeSlideUp();
}

function removeSlideUp() {
  return new Promise(function (resolve, reject) {
    if (!smartAlert.classList.contains("slideup")) {
      smartAlert.classList.add("slideup");
      smartAlert.addEventListener(
        "transitionend",
        function () {
          console.log("completed");
          smartAlert.classList.remove("slideup");
          resolve();
        },
        { once: true }
      );
    } else {
      smartAlert.classList.remove("slideup");
      // 移除类时也等待transitionend,保证队列顺序执行
      smartAlert.addEventListener(
        "transitionend",
        function () {
          resolve();
        },
        { once: true }
      );
    }
  });
}

// 点击事件:将任务加入队列
buttonShow.addEventListener("click", () => {
  queuePromise = queuePromise.then(() => slideUp());
});

参考优化版(基于Stack Overflow方案)

如果需要更灵活的动画切换(显示/隐藏),可以参考以下代码,同时修正了原参考代码的拼写错误(transitoinEnd改为transitionEnd):

const buttonShow = document.getElementById("btn_show");
const buttonHide = document.getElementById("btn_hide");
const smartAlert = document.getElementById("smart-alert");

// 执行队列
let queuePromise = Promise.resolve();

// 监听transitionend的通用函数
const transitionEnd = (element, message) => {
  return new Promise((resolve) => {
    const listener = () => {
      element.removeEventListener("transitionend", listener);
      resolve(message);
    };
    element.addEventListener("transitionend", listener);
  });
};

// 显示按钮点击逻辑
buttonShow.addEventListener("click", () => {
  queuePromise = queuePromise.then(async () => {
    let result;
    if (smartAlert.classList.contains("slideup")) {
      smartAlert.classList.remove("slideup");
      result = await transitionEnd(smartAlert, "提示框显示完成");
      console.log(result);
    } else {
      smartAlert.classList.add("slideup");
      result = await transitionEnd(smartAlert, "提示框隐藏完成");
      console.log(result);
      setTimeout(() => {
        smartAlert.classList.remove("slideup");
      }, 1000);
      result = await transitionEnd(smartAlert, "提示框显示完成");
      console.log(result);
    }
  });
});

// 隐藏按钮点击逻辑
buttonHide.addEventListener("click", () => {
  queuePromise = queuePromise.then(async () => {
    smartAlert.classList.add("slideup");
    const result = await transitionEnd(smartAlert, "提示框隐藏完成");
    console.log(result);
  });
});

关键说明

  • queuePromise变量始终保存当前队列的最后一个Promise,每次点击都通过.then()追加新任务,确保顺序执行。
  • 所有涉及过渡动画的操作(添加/移除类)都必须等待transitionend事件后再resolve,否则队列会提前执行下一个任务。
  • 使用{ once: true }或手动移除事件监听器,避免重复监听导致的逻辑混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 02:10:51