/* Page prose (zh-Hans) */
"VersionBModern" = "# 版本 B（现代）：`async`/`await` + `async let`\n\n同一个任务。每一次获取现在都是一个**异步函数**，它要么返回一个值，要么 `throws`。我们用 **`async let`** 启动两者，让它们并发运行，一起 `await` 两者，合并，然后让 `@MainActor` 把我们放到主线程上，不需要手动跳转。";
"VersionBModern-2" = "**观察运行：** 输出完全相同：\n\n* callout(输出):\n`Fetching menu and stock...`  \n`Sellable today: [\"Latte\", \"Espresso\"]`\n\n而且时间也完全相同（两者仍然并行运行，总共约 0.6 秒）。当 `Task` 完成时，Playground 页面会持续运行。\n\n* callout(试一试): 把 `fetchMenu` 的 sleep 提高到 `.milliseconds(1200)`：总时间跟随*最慢*的那次获取，而不是两者之和。哪一行仍然先打印？\n\n现在看看消失了什么：\n\n* **没有共享的可变变量。** `menu` 和 `stock` 就只是值。\n* **没有 `enter`/`leave`。** `async let` 启动每个子任务；`try await (menu, stock)` 就是汇合。没有要平衡的计数器。\n* **一条错误路径。** 无论是 `fetchMenu` 还是 `fetchStock` 抛出错误，都落在那单独一个 `catch` 里。你没有解包两个可选值，也没有调用 `.get()`。\n* **没有手动的主线程跳转。** 函数上的 `@MainActor` *就是*那个保证。合并和 `print` 在 main actor 上运行，因为函数被隔离到了它上面。忘记返回主线程已经不再可能。\n\n逻辑，也就是那个 `filter`，现在是函数里最显眼的一行，因为它周围的一切都让开了路。\n\n---\n\n## 感受差异\n\n* **共享的可变状态。** 经典：两个 `Result?` 变量。现代：没有。\n* **“等待两者”。** 经典：带 `enter`/`leave` 的 `DispatchGroup`。现代：带 `await` 的 `async let`。\n* **错误处理。** 经典：解包可选值，然后 `try`/`catch`。现代：一个 `try`/`catch`。\n* **主线程交付。** 经典：记得 `notify(queue: .main)`。现代：`@MainActor`，自动完成。\n* **不属于真正任务的代码行。** 经典：很多。现代：很少。\n* **编译器抓住漏掉的主线程跳转。** 经典：不会。现代：会。\n\n同样的行为，同样的并行，同样的输出，但 B 说出了它*意味着*什么，并让编译器强制执行那些你过去必须记在脑子里的部分。这就是用一个例子讲清楚的、支持现代模型的全部论据。\n\n[上一页](@previous)  |  第 4 / 8 页  |  [下一页: 经典与现代的实践](@next)";
