swift-async-operations

logo

swift-async-operations
一个扩展异步操作能力的库。

动机

Swift 并发是一个强大的语言特性,但是没有用于 Swift 并发操作数组的 API。开发者需要编写冗余代码。

var results: [Int] = [] // ☹️ var is required.
for await element in [0, 1, 2, 3, 4] {
    let newElement = try await twice(element)
    result.append(newElement)
}
print(results) // [0, 2, 4, 6, 8]

在需要循环并发执行的情况下,开发者需要编写更多冗余代码。

 // ☹️ Long redundant code
let array = [0, 1, 2, 3, 4]
let results = try await withThrowingTaskGroup(of: (Int, Int).self) { group in
    for (index, number) in array.enumerated() {
        group.addTask {
            (index, try await twice(number))
        }
    }
    var results: [Int: Int] = [:]
    for try await (index, result) in group {
        results[index] = result
    }
    // ☹️ Need to take the order into account.
    return results.sorted(by: { $0.key < $1.key }).map(\.value)
}
print(results) // [0, 2, 4, 6, 8]

解决方案

这个库提供了异步函数作为 Sequence 的扩展,例如 asyncMap

let converted = try await [0, 1, 2, 3, 4].asyncMap { number in
    try await twice(number)
}
print(converted) // [0, 2, 4, 6, 8]

默认情况下,闭包顺序执行。通过指定最大任务数,闭包也可以并发执行。

let converted = try await [0, 1, 2, 3, 4].asyncMap(numberOfConcurrentTasks: 8) { number in
    try await twice(number)
}
print(converted) // [0, 2, 4, 6, 8]

功能详情

这个库提供了两个功能。

  1. Sequence 的异步函数。
  2. 有序任务组

Sequence 的异步函数

这个库提供了异步操作,例如 asyncForEachasyncMap

try await [1, 2, 3].asyncForEach { number in
    print("Start: \(number)")
    try await doSomething(number)
    print("End: \(number)")
}

默认情况下,闭包顺序执行。

Start: 1
End: 1
Start: 2
End: 2
Start: 3
End: 3

作为高级用法,可以指定 numberOfConcurrentTasks,如果该值大于等于 2,闭包可以并行运行。

try await [1, 2, 3].asyncForEach(numberOfConcurrentTasks: 3) { number in
    print("Start: \(number)")
    try await doSomething(number)
    print("End: \(number)")
}
Start: 2
End: 2
Start: 1
Start: 3
End: 3
End: 1

扩展函数即使对于像 map 函数这样对顺序敏感的函数也执行并行操作,在转换数组的同时保留原始顺序。

let result = try await [1, 2, 3].asyncMap(numberOfConcurrentTasks: 3) { number in
    print("Start: \(number)")
    let result = try await twice(number)
    print("End: \(number)")
    return result
}
print(result)
Start: 1
Start: 3
End: 3
End: 1
Start: 2
End: 2
[2, 4, 6]

这个库提供了

有序任务组

原始的实用函数 withTaskGroupwithThrowingTaskGroup 不保证 for await 的顺序。

let results = await withTaskGroup(of: Int.self) { group in
    (0..<5).forEach { number in
        group.addTask {
            await Task.yield()
            return number * 2
        }
    }
    var results: [Int] = []
    for await number in group {
        results.append(number)
    }
    return results
}
print(results) // ☹️ [0, 4, 2, 6, 10, 8]

然而,在某些情况下,例如将数组转换为新数组时,需要有序的 for await

withOrderedTaskGroupwithThrowingOrderedTaskGroup 满足这些需求。

let results = await withOrderedTaskGroup(of: Int.self) { group in
    (0..<5).forEach { number in
        group.addTask {
            await Task.yield()
            return number * 2
        }
    }
    var results: [Int] = []
    for await number in group {
        results.append(number)
    }
    return results
}
print(results) // 😁 [0, 2, 4, 6, 8, 10]

它们也被用于 Sequence 的异步函数。

要求

Swift 5.10 或更高版本。

安装

你可以通过 Swift Package Manager 安装这个库。

dependencies: [
  .package(url: "https://github.com/mtj0928/swift-async-operations", from: "0.1.0")
]

文档

请查看 DocC 文档页面