构建

两个人都收到「已预订」 — 一个 await 分开的东西

两个人同时抢同一个时段。用话说,这两个处理器做的是完全一样的事 — 看看空不空,空就占。一个给两个人都发了确认,另一个没有。差别就是中间那一个 await。

作者: makemind · 2026年7月13日

预订工具里最糟的失败不是订不上。

是给两个人都发「您已预订成功」。 订不上可以再订一次;两个人都被告知订上了,接下来就得靠人打电话去摆平。

两个处理器,同一段描述

这个样例里有两个「占用时段」的处理器。用话描述完全一样 —看看空不空,空就占。

一个是这样。

// 1. Look.
final free = slot.takenBy == null;

// 2. Anything at all in here — a database round trip, a payment check, a
//    log write, a lookup of the customer's name — hands control to
//    whatever else was in flight.
await Future<void>.delayed(Duration.zero);

// 3. Write, using what was true in step 1.
if (free) { slot.takenBy = who; ... }

另一个是这样。

if (slot.takenBy != null) {
  return '$at is already taken by ${slot.takenBy}';
}
slot.takenBy = who;
slot.confirmations.add(who);

await Future<void>.delayed(Duration.zero); // the slow part, after the claim

同一个 await,只是位置不同。 前者在「看」和「写」之,后者在「写」之

而这个位置制造了重复预订。

先让它真的发生

unsafe -> Suh:  "CONFIRMED 10:30 for Suh"
unsafe -> Park: "CONFIRMED 10:30 for Park"
unsafe result: 1 slot(s) confirmed to more than one person
竞争之后 —— 10:30 这一个时段有两个人都被告知 yes。TOLD YES TWICE 为 1,那一行里两个名字都还在
竞争之后 —— 10:30 这一个时段有两个人都被告知 yes。TOLD YES TWICE 为 1,那一行里两个名字都还在

画面精确地展示了现实中的症状。 时段的主人只有 Park 一个,而被告知「订上了」的人有两个。

这就是真实店铺里发生的样子。账上是一个人,门口来了两个。

所以服务器把所有发过确认的人都记着。

/// Everybody who has ever been told they got this slot. In a correct
/// booking system this never has more than one name in it, which is exactly
/// why it is worth keeping.
final confirmations = <String>[];

正常的话,它永远是一个名字的列表。永远只该有一条的列表,正是值得去数的列表。

然后让它不发生

同样两个人、同样的时段,只把那个缝隙关上。

--- cleared, same two people, same slot, gap closed ---
safe -> Suh:  "CONFIRMED 10:30 for Suh"
safe -> Park: "10:30 is already taken by Suh"
safe result: nobody was told yes twice
同样的竞争,服务端一次只放行一个 —— 10:30 只属于 Suh,TOLD YES TWICE 为 0。页脚是 &quot;One slot, one yes&quot;
同样的竞争,服务端一次只放行一个 —— 10:30 只属于 Suh,TOLD YES TWICE 为 0。页脚是 "One slot, one yes"

拒绝不是「失败了」,而是「Suh 订走了」。 第二个人需要知道的不是自己失败了,而是这个位子没了 — 这样他才能去挑下一个时间。

此内容需要开发者或更高等级

登录并升级您的方案即可继续阅读。

查看方案
Twitter