大数跨境

使用 @TaskLocal 进行 Swift 任务本地存储

使用 @TaskLocal 进行 Swift 任务本地存储 索引目录
2025-08-05
2
导读:关注【索引目录】服务号,更多精彩内容等你来探索!Swift 中的任务是什么?可以将任务想象成一个盒子,里面装着应用需要完成的一些工作。

关注【索引目录】服务号,更多精彩内容等你来探索!

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

这里发生了什么事?

  1. 两个部门(HR 和 IT)同时处理请求
  2. 每个任务都知道它属于哪个部门
  3. 主办公室(任何任务之外)显示“未分配”
  4. 每个部门在整个过程中保持其身份
  5. 处理是并行进行的,因此顺序可能会有所不同

示例 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

这里发生了什么事?

  1. 我们设置一个包含多个值的上下文
  2. 主函数和子任务(async let)都可以访问所有值
  3. 无需参数——一切都自动通过任务层次结构!

这为什么令人惊奇?

如果没有 @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!
}

主要优点

  1. 不再需要传递参数
    - 设置一次,随处使用
  2. 自动继承
    - 子任务自动获取值
  3. 干净的代码
    ——函数不需要额外的参数
  4. 安全
    - 值仅在正确的范围内可用

想象一下@TaskLocal一个有用的助手,它会自动将重要信息传递给每个需要它的人,所以您不必这样做!


关注【索引目录】服务号,更多精彩内容等你来探索!


【声明】内容源于网络
0
0
索引目录
索引目录是一家专注于医疗、技术开发、物联网应用等领域的创新型公司。我们致力于为客户提供高质量的服务和解决方案,推动技术与行业发展。
内容 444
粉丝 0
索引目录 索引目录是一家专注于医疗、技术开发、物联网应用等领域的创新型公司。我们致力于为客户提供高质量的服务和解决方案,推动技术与行业发展。
总阅读1.3k
粉丝0
内容444