Fork

并行化两个或多个异步函数

什么是 Fork?

Fork 是一个 Swift 6 库,允许并行化多个异步函数。它提供了一个 Fork 结构体,该结构体接受单个输入,并将其拆分为两个独立的异步函数,这两个函数返回不同的输出。然后可以将这两个函数合并为一个函数,该函数返回单个输出。

为什么使用 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 也将并行运行。

对象

基本用法

import Fork

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

ForkedArray 使您可以轻松地对 Array 中的所有元素执行异步函数。ForkedArray 有助于上面的示例

let forkedArray = ForkedArray(photoNames, map: downloadPhoto(named:))
let photos = try await forkedArray.output()

BatchedForkedArray

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 循环中,每个照片名称批次都会被异步处理。然后,我们迭代批次中的每张照片并相应地执行操作。这允许以批次形式高效处理大型数据集,同时控制同时运行的并行进程数量。

ForkedActor 示例

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)

ForkedActor KeyPathActor 示例

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)

更多示例

使用 Fork 的 Swift 包

SwishXCAssets

在随附的 XCAssets 文件结构中,并发地从 SVG 为您的 iOS 应用程序生成 App Icon。