关注【索引目录】服务号,更多精彩内容等你来探索!
Swift 中的任务是什么?
可以将任务想象成一个盒子,里面装着应用需要完成的一些工作。在 Swift 的 async/await 世界中,当你编写异步函数时,你实际上是在创建这些可以同时运行的工作盒子。
func parentFunction() async {
// This is the main task (parent)
async let child1 = childFunction1() // Creates a child task
async let child2 = childFunction2() // Creates another child task
await [child1, child2] // Wait for both children to finish
}
父任务会创建子任务,这些子任务组成一个“家族树”。父任务会等待子任务完成后才能完成。
什么是@TaskLocal?
@TaskLocal就像一个神奇的背包,会自动从父任务传递给子任务。你只需将物品放入背包,所有子任务就能看到里面的内容,无需你手动将物品传递给每个子任务。
简单规则:
-
必须声明为 static -
自动与子任务共享 -
仅在任务运行时存在
示例 1:部门处理系统
让我们创建一个完全不同的场景。假设你正在构建一个系统,其中不同的部门同时处理请求:
enum Department {
@TaskLocal static var currentDepartment: String = "Unassigned"
}
func processRequests() async {
// Create tasks for different departments
let hrTask = Task {
await Department.$currentDepartment.withValue("HR") {
print(" \(Department.currentDepartment) department started processing")
try? await Task.sleep(nanoseconds: 500_000) // Simulate work
print(" \(Department.currentDepartment) department finished processing")
}
}
let itTask = Task {
await Department.$currentDepartment.withValue("IT") {
print(" \(Department.currentDepartment) department started processing")
try? await Task.sleep(nanoseconds: 700_000) // Simulate work
print(" \(Department.currentDepartment) department finished processing")
}
}
// Check what department we're in outside any task scope
print(" Main office department: \(Department.currentDepartment)")
// Wait for all departments to finish
await hrTask.value
await itTask.value
}
运行此代码时,您可能会看到如下输出:
IT department started processing
HR department started processing
Main office department: Unassigned
HR department finished processing
IT department finished processing
这里发生了什么事?
-
两个部门(HR 和 IT)同时处理请求 -
每个任务都知道它属于哪个部门 -
主办公室(任何任务之外)显示“未分配” -
每个部门在整个过程中保持其身份 -
处理是并行进行的,因此顺序可能会有所不同
示例 2:具有嵌套任务的多个值
现在让我们看看如何@TaskLocal存储多个值并与子任务一起工作:
struct AppContext {
let userID: String
let sessionToken: String
let requestID: String
}
enum AppSession {
@TaskLocal static var context: AppContext?
}
func handleAPIRequest() async {
let appContext = AppContext(
userID: "alice123",
sessionToken: "token_abc",
requestID: "req_789"
)
await AppSession.$context.withValue(appContext) {
await processRequest()
}
}
func processRequest() async {
logRequest()
// Create child tasks - they inherit the context automatically
async let dataFetch = fetchUserData()
async let validation = validateRequest()
await [dataFetch, validation]
logCompletion()
}
func logRequest() {
guard let ctx = AppSession.context else { return }
print("Starting request \(ctx.requestID) for user \(ctx.userID)")
}
func fetchUserData() async {
guard let ctx = AppSession.context else { return }
print("Fetching data for user: \(ctx.userID) with token: \(ctx.sessionToken)")
}
func validateRequest() async {
guard let ctx = AppSession.context else { return }
print("Validating request: \(ctx.requestID)")
}
func logCompletion() {
guard let ctx = AppSession.context else { return }
print("Completed request \(ctx.requestID) for user \(ctx.userID)")
}
让我们运行这个:
await handleAPIRequest()
输出:
Starting request req_789 for user alice123
Fetching data for user: alice123 with token: token_abc
Validating request: req_789
Completed request req_789 for user alice123
这里发生了什么事?
-
我们设置一个包含多个值的上下文 -
主函数和子任务( async let)都可以访问所有值 -
无需参数——一切都自动通过任务层次结构!
这为什么令人惊奇?
如果没有 @TaskLocal,你就必须这样做:
func processAPIRequest(user: UserContext) async {
await checkPermissions(user: user)
await fetchUserData(user: user)
await logActivity(user: user)
}
func checkPermissions(user: UserContext) async { ... }
func fetchUserData(user: UserContext) async { ... }
func logActivity(user: UserContext) async { ... }
您必须将用户传递给每一个功能!
使用 @TaskLocal,您只需设置一次即可忘记它:
await $currentUser.withValue(user) {
await processAPIRequest() // No parameters needed!
}
主要优点
- 不再需要传递参数
- 设置一次,随处使用 - 自动继承
- 子任务自动获取值 - 干净的代码
——函数不需要额外的参数 - 安全
- 值仅在正确的范围内可用
想象一下@TaskLocal一个有用的助手,它会自动将重要信息传递给每个需要它的人,所以您不必这样做!
关注【索引目录】服务号,更多精彩内容等你来探索!

