并行化两个或多个异步函数
Fork 是一个 Swift 6 库,允许并行化多个异步函数。它提供了一个 Fork 结构体,该结构体接受单个输入,并将其拆分为两个独立的异步函数,这两个函数返回不同的输出。然后可以将这两个函数合并为一个函数,该函数返回单个输出。
使用 async-await 语法可以更轻松地进行 Swift 中的异步编程,但并行化多个函数仍然可能具有挑战性。Fork 通过允许开发人员轻松创建并行任务来简化此操作。
Swift Book 提供了以下下载多个图像的示例。
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
现在,上面的代码仍然是异步的,但一次只会运行一个函数。在上面的示例中,将首先设置 firstPhoto
,然后是 secondPhoto
,最后是 thirdPhoto
。
要并行运行这三个异步函数,我们需要将代码更改为以下示例。
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
上面的代码现在将同时下载所有三张照片。当所有照片都下载完毕后,它将显示这些照片。
现在,使用 Fork 我们可以将其简化为几行代码!
let photos = try await photoNames.asyncMap(downloadPhoto(named:))
show(photos)
使用 Fork 时,函数将并行运行,并且更高阶的 fork 也将并行运行。
Fork
:使用单个输入创建两个独立的异步函数,它们返回 LeftOutput
和 RightOutput
。ForkedArray
:使用单个数组和单个异步函数,并行化数组中每个值的操作。BatchedForkedArray
:使用单个数组和单个异步函数,批量处理数组中每个值的并行化操作ForkedActor
:使用单个 actor 创建两个独立的异步函数,这些函数被传递给该 actor。KeyPathActor
:一个通用的 Actor,它使用 KeyPath 来更新和设置值。import Fork
let fork = Fork(
value: 10,
leftOutput: { $0.isMultiple(of: 2) },
rightOutput: { "\($0)" }
)
let leftOutput = try await fork.left()
let rightOutput = try await fork.right()
XCTAssertEqual(leftOutput, true)
XCTAssertEqual(rightOutput, "10")
let output: String = try await fork.merged { bool, string in
if bool {
return string + string
}
return string
}
let output = await mergedFork()
XCTAssertEqual(output, "1010")
ForkedArray 使您可以轻松地对 Array 中的所有元素执行异步函数。ForkedArray 有助于上面的示例。
let forkedArray = ForkedArray(photoNames, map: downloadPhoto(named:))
let photos = try await forkedArray.output()
BatchedForkedArray
允许您使用异步函数有效地并行化和批量处理值数组。它提供了用于在单个输出中解析并行化数组以及流式传输已解析数组批次的方法。
let batchedForkedArray = BatchedForkedArray(photoNames, batch: 3, map: downloadPhoto(named:))
let photos = try await forkedArray.output()
在上面的示例中,我们创建了一个 BatchedForkedArray
的实例,批处理大小为 3,并将 downloadPhoto 函数作为 map 闭包。
要解析批量数组,我们使用 output()
方法,该方法并行执行每个照片名称批次的 downloadPhoto 函数。解析完成后,photos
数组将包含已下载的照片,顺序与处理顺序相同。
let photoNames = [Int](0 ..< 100)
let batchedForkedArray = BatchedForkedArray(
photoNames,
batch: 5,
map: downloadPhoto(named:)
)
for try await batch in batchedForkedArray.stream() {
for photo in batch {
// Perform operations on each photo in the batch
print(photo)
}
}
在此示例中,我们创建了一个 BatchedForkedArray
的实例,批处理大小为 5,并将 downloadPhoto(named:)
函数作为 map 闭包。通过使用 stream()
方法,我们可以异步迭代照片名称批次。
在 for-await 循环中,每个照片名称批次都会被异步处理。然后,我们迭代批次中的每张照片并相应地执行操作。这允许以批次形式高效处理大型数据集,同时控制同时运行的并行进程数量。
actor TestActor {
var value: Int = 0
func increment() {
value += 1
}
}
let forkedActor = ForkedActor(
actor: TestActor(),
leftOutput: { actor in
await actor.increment()
},
rightOutput: { actor in
try await actor.fork(
leftOutput: { await $0.increment() },
rightOutput: { await $0.increment() }
)
.act()
}
)
let actorValue = await forkedActor.act().value
XCTAssertEqual(actorValue, 3)
let forkedActor = ForkedActor(
value: 0,
leftOutput: { actor in
await actor.update(to: { $0 + 1 })
},
rightOutput: { actor in
try await actor.fork(
leftOutput: { actor in
await actor.update(to: { $0 + 1 })
},
rightOutput: { actor in
await actor.update(\.self, to: { $0 + 1 })
}
)
.act()
}
)
let actorValue = try await forkedActor.act().value
XCTAssertEqual(actorValue, 3)
在随附的 XCAssets 文件结构中,并发地从 SVG 为您的 iOS 应用程序生成 App Icon。