Skip to content

一、问题背景

我们有一个统一的支付入口 preOrderPay,内部流程大致是:

postPreOrder -> requestPayment -> getPayResult

多个页面都会调用它。

**方法调用已经加了 1s 的防抖,但是还是存在重复调用的情况** 问题在于:

如果用户在弱网的情况下点击两次支付按钮,触发时间可能由于网络延迟超出防抖限制时间,就会触发两次 preOrderPay

于是就可能出现:

  • 两次 postPreOrder(生成两个订单)
  • 两次 requestPayment(拉起两次支付)
  • 状态难以控制

二、解决方案:Promise 单飞

核心思路非常简单:

同一时间只允许存在一个“进行中的支付 Promise”

实现方式

在模块级维护一个变量:

let currentPreOrderPayPromise: Promise<any> | null = null;

然后改造 preOrderPay

export function preOrderPay() {
// 如果已经有支付在进行,直接复用
if (currentPreOrderPayPromise) {
return currentPreOrderPayPromise;
}

currentPreOrderPayPromise = (async () => {
try {
const order = await postPreOrder();
await requestPayment(order);
return await getPayResult(order);
} finally {
// 无论成功失败,都释放
currentPreOrderPayPromise = null;
}
})();

return currentPreOrderPayPromise;
}

三、执行流程拆解

第一次点击

  1. currentPreOrderPayPromise === null
  2. 创建一个完整支付 Promise
  3. 赋值给 currentPreOrderPayPromise
  4. 开始执行支付流程

第二次点击(关键点)

  1. 发现 currentPreOrderPayPromise 已存在
  2. 不再重新执行支付流程
  3. 直接 return 这个 Promise

👉 结果就是:

两次调用共享同一个支付结果


支付结束

finally 中:

currentPreOrderPayPromise = null;

👉 下一次支付才允许重新开始


四、这个方案本质在做什么?

不是“按钮防抖”,而是:

在“支付公共入口”做串行化控制

核心能力是:

  • 同一时间只允许一条支付链路执行
  • 后续请求不会新开流程,而是复用
  • 流程结束后再放行

五、和“传统加锁”的区别

很多人第一反应是用一个布尔锁:

if (isPaying) {
return toast('请勿重复点击');
}
isPaying = true;

try {
...
} finally {
isPaying = false;
}

两种方案的本质区别:

方案 | 后续请求处理方式 -- | -- 普通锁(isPaying) | ❌ 直接拒绝 Promise 单飞 | ✅ 合并请求

六、为什么支付场景更适合“单飞”?

1. 重复点击 ≠ 新需求

用户点两次支付,本质上还是同一次支付行为

👉 所以更合理的方式是“合并”,而不是“拒绝”。


2. 对调用方更友好

普通锁会带来:

  • 需要处理“已锁定”的异常
  • UI 逻辑复杂

而单飞:

  • 所有调用方拿到的都是同一个 Promise
  • 不需要额外分支处理

3. 改动更小,覆盖更全

只需要改造 preOrderPay

  • 所有页面自动生效
  • 不需要逐个按钮处理

七、局限性

这个方案也不是完美的。

❗ 内存级锁

let currentPreOrderPayPromise = null;

意味着:

  • 只在当前小程序实例有效
  • 页面重进 / 进程重建后失效

👉 不能作为跨会话的幂等保障


八、最佳实践(推荐组合)

实际项目中,不要二选一,而是组合使用:

✅ 公共层:Promise 单飞

  • 真正防住重复支付
  • 保证业务一致性

✅ 页面层:按钮禁用 / loading

  • 给用户明确反馈
  • 避免“疯狂点击”

九、总结

一句话总结这个方案:

用 Promise 把“重复请求”变成“共享结果”

相比传统加锁:

  • 更优雅
  • 更贴近业务语义
  • 对调用方更友好