此源码解析适用于
0.41.0
之前的版本。最新的源码已经把Reducer
重构成了一个协议ReducerProtocol
,但核心的实现方法还是离不开这个版本的源码。
这个扩展的作用是将普通 Effect
变成可取消的。无论 Effect
的 operation
是 .publisher
还是 .run
,它们的核心逻辑都是利用 AnyCancellable
来完成。
extension Effect {
public func cancellable(id: AnyHashable, cancelInFlight: Bool = false) -> Self {
switch self.operation {
case .none:
return .none
case let .publisher(publisher):
return Self(
operation: .publisher(
// `Deferred` 可以让闭包在接收到订阅者时才执行
Deferred {
()
-> Publishers.HandleEvents<
Publishers.PrefixUntilOutput<
AnyPublisher<Action, Failure>, PassthroughSubject<Void, Never>
>
> in
// 加锁,保证相关数据的准确性
cancellablesLock.lock()
defer { cancellablesLock.unlock() }
let id = CancelToken(id: id)
if cancelInFlight {
// 取消正在运行中的 `Effect`
cancellationCancellables[id]?.forEach { $0.cancel() }
}
// 用于取消正在运行中的 `Effect`
let cancellationSubject = PassthroughSubject<Void, Never>()
// 声明用于取消的 `AnyCancellable`
var cancellationCancellable: AnyCancellable!
// 创建用于取消的 `AnyCancellable`
cancellationCancellable = AnyCancellable {
// 这个代码块将在调用 `cancel()` 的时候调用
cancellablesLock.sync {
// `cancellationSubject` 发送一个值,并完成
cancellationSubject.send(())
cancellationSubject.send(completion: .finished)
// 将 `cancellationCancellable` 从 `id` 对应的集合中移除
cancellationCancellables[id]?.remove(cancellationCancellable)
// 如果 `id` 对应的集合为空,则把 `id` 对应的值设置为 `nil`
if cancellationCancellables[id]?.isEmpty == .some(true) {
cancellationCancellables[id] = nil
}
}
}
// 最终实现取消功能的核心在这里:当 `cancellationSubject` 发出一个值后,`publisher` 就不会再发出值。
// 所以上面的 `AnyCancellable` 调用 `cancel()` 后,就可以达到取消的目的。
return publisher.prefix(untilOutputFrom: cancellationSubject)
.handleEvents(
receiveSubscription: { _ in
_ = cancellablesLock.sync {
// 接收到订阅,把 `cancellationCancellable` 缓存起来
cancellationCancellables[id, default: []].insert(
cancellationCancellable
)
}
},
// 接收到完成或取消,都调用 `cancellationCancellable.cancel()`,完成数据清理
receiveCompletion: { _ in cancellationCancellable.cancel() },
receiveCancel: cancellationCancellable.cancel
)
}
.eraseToAnyPublisher()
)
)
case let .run(priority, operation):
return Self(
operation: .run(priority) { send in
// 调用了一个具体实现的方法,查看此方法解析
await withTaskCancellation(id: id, cancelInFlight: cancelInFlight) {
await operation(send)
}
}
)
}
}
// 上面那个方法的重载方法,这里的 `id` 是一个 `Any.Type`
public func cancellable(id: Any.Type, cancelInFlight: Bool = false) -> Self {
self.cancellable(id: ObjectIdentifier(id), cancelInFlight: cancelInFlight)
}
// 取消对应 `id` 的所有正在进行中的 Effect
public static func cancel(id: AnyHashable) -> Self {
.fireAndForget {
cancellablesLock.sync {
cancellationCancellables[.init(id: id)]?.forEach { $0.cancel() }
}
}
}
// 上面那个方法的重载方法,这里的 `id` 是一个 `Any.Type`
public static func cancel(id: Any.Type) -> Self {
.cancel(id: ObjectIdentifier(id))
}
// 取消 `ids` 数组元素对应的所有正在进行中的 Effect
public static func cancel(ids: [AnyHashable]) -> Self {
// 每个 `id` 转换为一个 Effect,然后再合并
.merge(ids.map(Effect.cancel(id:)))
}
// 上面那个方法的重载方法,这里的 `ids` 是一个 `Any.Type` 数组
public static func cancel(ids: [Any.Type]) -> Self {
.merge(ids.map(Effect.cancel(id:)))
}
}
public func withTaskCancellation<T: Sendable>(
id: AnyHashable,
cancelInFlight: Bool = false,
operation: @Sendable @escaping () async throws -> T
) async rethrows -> T {
// 创建任务
let task = { () -> Task<T, Error> in
// 加锁,保证相关数据的准确性
cancellablesLock.lock()
let id = CancelToken(id: id)
if cancelInFlight {
// 取消正在运行中的 `Effect`
cancellationCancellables[id]?.forEach { $0.cancel() }
}
// 创建任务
let task = Task { try await operation() }
// 声明用于取消的 `AnyCancellable`
var cancellable: AnyCancellable!
// 创建用于取消的 `AnyCancellable`
cancellable = AnyCancellable {
// 这个代码块将在调用 `AnyCancellable.cancel()` 的时候调用
// 取消任务
task.cancel()
cancellablesLock.sync {
// 将 `cancellable ` 从 `id` 对应的集合中移除
cancellationCancellables[id]?.remove(cancellable)
// 如果 `id` 对应的集合为空,则把 `id` 对应的值设置为 `nil`
if cancellationCancellables[id]?.isEmpty == .some(true) {
cancellationCancellables[id] = nil
}
}
}
// 把 `cancellable` 缓存起来
cancellationCancellables[id, default: []].insert(cancellable)
cancellablesLock.unlock()
return task
}()
do {
// 执行异步任务
return try await task.cancellableValue
} catch {
// 如果执行出错,则重新抛出错误
return try Result<T, Error>.failure(error)._rethrowGet()
}
}
// 上面那个方法的重载方法,这里的 `id` 是一个 `Any.Type`
public func withTaskCancellation<T: Sendable>(
id: Any.Type,
cancelInFlight: Bool = false,
operation: @Sendable @escaping () async throws -> T
) async rethrows -> T {
try await withTaskCancellation(
id: ObjectIdentifier(id),
cancelInFlight: cancelInFlight,
operation: operation
)
}
extension Task where Success == Never, Failure == Never {
// `Task` 的扩展方法,取消对应 `id` 的所有正在进行中副作用
public static func cancel<ID: Hashable & Sendable>(id: ID) async {
// 在使用 `Task.cancel(id:)` 的地方,通常是伴随着 `async/await` 的使用,
// 所以把这个方法定义为一个 `async` 方法。
// 为了把 cancel 的操作放在 `async/await` 的环境下执行,就使用了 `MainActor.run`。
await MainActor.run {
cancellablesLock.sync { cancellationCancellables[.init(id: id)]?.forEach { $0.cancel() } }
}
}
// 上面那个方法的重载方法,这里的 `id` 是一个 `Any.Type`
public static func cancel(id: Any.Type) async {
await self.cancel(id: ObjectIdentifier(id))
}
}
// 作为 `cancellationCancellables` 字典的 Key
struct CancelToken: Hashable {
let id: AnyHashable
// 不知道 `discriminator` 存在的意义是什么?`id` 已经足够保证唯一性。
let discriminator: ObjectIdentifier
init(id: AnyHashable) {
self.id = id
self.discriminator = ObjectIdentifier(type(of: id.base))
}
}
// 存储 cancellables
var cancellationCancellables: [CancelToken: Set<AnyCancellable>] = [:]
// 递归锁,保证 cancellables 相关数据的准确性。
// 使用递归锁的原因是在 `Store.send(_:originatingFrom)` 方法中有递归操作。
let cancellablesLock = NSRecursiveLock()
// 定义 `_ErrorMechanism` 是为了让 `Result` 拥有 `_rethrowGet` 方法,方便重抛错误。
// 在 `withTaskCancellation` 的具体实现中用到。
@rethrows
private protocol _ErrorMechanism {
associatedtype Output
func get() throws -> Output
}
extension _ErrorMechanism {
internal func _rethrowError() rethrows -> Never {
_ = try _rethrowGet()
fatalError()
}
internal func _rethrowGet() rethrows -> Output {
return try get()
}
}
extension Result: _ErrorMechanism {}