此源码解析适用于
0.41.0
之前的版本。最新的源码已经把Reducer
重构成了一个协议ReducerProtocol
,但核心的实现方法还是离不开这个版本的源码。
Effect
类型封装了可以在外部运行的工作单元,并且可以将 Action
反馈到 Store
。这是一个用来做副作用的地方,如网络请求等。
Effect
是从 Reducer
返回的,所以 Store
可以在 Reducer
处理完毕后执行 Effect
。
有两种不同的方法来创建 Effect
:
-
使用 Swift 自带的并发。根据不同需求,有三种方式创建
Effect
:task(priority:operation:catch:file:fileID:line:)
:想要返回一个Action
。run(priority:operation:catch:file:fileID:line:)
:想要返回任意数量的Action
。fireAndForget(priority:_:)
:不返回任何Action
。
-
使用
Combine
框架。利用Combine
的特性实现需求,最终利用eraseToEffect
或者catchToEffect
将Publisher
转换为Effect
。注意:这种方式是不推荐的,所以最终你应该使用 Swift 自带的并发来创建Effect
。
重要:
Store
不是线程安全的,因此所有Effect
必须在同一个线程上接收值,通常是主线程。这是使用Combine
框架仅有的一个问题。如果使用 Swift 并发和 Effect 的三种方法.task
、.run
和.fireAndForget
,则线程问题由 Swift 自动处理。
Effect
的初始化
初始化 Effect
需要传入一个 Operation
。也就是要告诉这个副作用需要做什么操作。每一种操作的解析如下:
.none
:不需要做任何操作。.publisher(AnyPublisher<Action, Failure>)
:操作类型是Combine
框架的AnyPublisher
。.run(TaskPriority? = nil, @Sendable (Send<Action>) async -> Void)
:操作类型是执行Concurrency
框架的Task
。
public struct Effect<Action, Failure: Error> {
@usableFromInline
enum Operation {
case none
case publisher(AnyPublisher<Action, Failure>)
case run(TaskPriority? = nil, @Sendable (Send<Action>) async -> Void)
}
@usableFromInline
let operation: Operation
@usableFromInline
init(operation: Operation) {
self.operation = operation
}
}
Send
类型
用于把 Action
发送给系统,类似于 Redux 中的 Dispatch
。
@MainActor
public struct Send<Action> {
public let send: @MainActor (Action) -> Void
public init(send: @escaping @MainActor (Action) -> Void) {
self.send = send
}
public func callAsFunction(_ action: Action) {
guard !Task.isCancelled else { return }
self.send(action)
}
public func callAsFunction(_ action: Action, animation: Animation?) {
guard !Task.isCancelled else { return }
withAnimation(animation) {
self(action)
}
}
}
-
@MainActor
:它来自于Concurrency
框架,是一个全局actor
。被它标记的类型、变量等在运行过程中的相关操作,都会在主线程中执行。 -
callAsFunction
:一个类型实现了这个方法后,可以直接对这个类型的实例进行调用。例如:
send(.started, animation: .spring())
想要详细了解,可以看文档 callAsFunction。
创建 Effect
none
: 不触发任何副作用。适用于必须返回一个Effect
,但不需要做任何副作用的时候。
public static var none: Self {
Self(operation: .none)
}
task
: 把只能处理一个Action
的异步任务包装在Effect
中。
public static func task(
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async throws -> Action,
catch handler: (@Sendable (Error) async -> Action)? = nil,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> Self {
Self(
operation: .run(priority) { send in
do {
// `operation()` 会返回一个 `Action`,
// send 会把它发送给系统处理
try await send(operation())
} catch is CancellationError {
// 忽略 `CancellationError`
return
} catch {
guard let handler = handler else {
#if DEBUG
// 省略 runtimeWarning
#endif
return
}
// 跟 `do` 代码块里面的 `send(operation())` 类似,
// 不同的是此处进行错误处理
await send(handler(error))
}
}
)
}
run
: 把可以处理任意数量Action
的异步任务包装在Effect
中。
public static func run(
priority: TaskPriority? = nil,
operation: @escaping @Sendable (Send<Action>) async throws -> Void,
catch handler: (@Sendable (Error, Send<Action>) async -> Void)? = nil,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> Self {
Self(
operation: .run(priority) { send in
do {
// 把 `send` 传给 `operation` 闭包,
// 这样它内部就可以发送任意数量的 `Action` 给系统
try await operation(send)
} catch is CancellationError {
// 忽略 `CancellationError`
return
} catch {
guard let handler = handler else {
#if DEBUG
// 省略 runtimeWarning
#endif
return
}
// 跟 `do` 代码块里面的 `operation(send)` 类似,
// 不同的是此处进行错误处理
await handler(error, send)
}
}
)
}
fireAndForget
: 把不需要发送Action
给系统的任务包装在Effect
中。
public static func fireAndForget(
priority: TaskPriority? = nil,
_ work: @escaping @Sendable () async throws -> Void
) -> Self {
// 内部直接调用了 `run` 方法
Self.run(priority: priority) { _ in try? await work() }
}
合并 Effect
// 把一组任意数量的 `effects` 合并成一个 `Effect`,并同时执行 `effects`
@inlinable
public static func merge(_ effects: Self...) -> Self {
Self.merge(effects)
}
// 把一个序列的 `effects` 合并成一个 `Effect`,并同时执行 `effects`
@inlinable
public static func merge<S: Sequence>(_ effects: S) -> Self where S.Element == Self {
effects.reduce(.none) { $0.merge(with: $1) }
}
// 把自己与另外一个 `Effect` 合并成一个新的 `Effect`,并同时执行
@inlinable
public func merge(with other: Self) -> Self {
switch (self.operation, other.operation) {
case (_, .none):
// 另外一个为 `none`,直接返回自己
return self
case (.none, _):
// 自己为 `none`,返回 `other`
return other
case (.publisher, .publisher), (.run, .publisher), (.publisher, .run):
// 只要至少其中一个为 `publisher`,就返回合并后的 `publisher`。
// 因为 `Effect` 实现了 `Publisher` 协议,
// 所以直接调用自带的 `Publishers.Merge` 进行合并
return Self(operation: .publisher(Publishers.Merge(self, other).eraseToAnyPublisher()))16:20
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
/// 两个都是 `run`,返回合并后的 `run`
return Self(
operation: .run { send in
// `withTaskGroup` 是 `Combine` 自带的方法,
// 把两个任务放到任务组中
await withTaskGroup(of: Void.self) { group in
group.addTask(priority: lhsPriority) {
await lhsOperation(send)
}
group.addTask(priority: rhsPriority) {
await rhsOperation(send)
}
}
}
)
}
}
// 把一组任意数量的 `effects` 串联成一个 `Effect`,并逐个执行 `effects`
@inlinable
public static func concatenate(_ effects: Self...) -> Self {
Self.concatenate(effects)
}
// 把一个集合的 `effects` 串联成一个 `Effect`,并逐个执行 `effects`
@inlinable
public static func concatenate<C: Collection>(_ effects: C) -> Self where C.Element == Self {
effects.reduce(.none) { $0.concatenate(with: $1) }
}
// 把自己与另外一个 `Effect` 串联成一个新的 `Effect`,并逐个执行
@inlinable
@_disfavoredOverload
public func concatenate(with other: Self) -> Self {
switch (self.operation, other.operation) {
case (_, .none):
// 另外一个为 `none`,直接返回自己
return self
case (.none, _):
// 自己为 `none`,返回 `other`
return other
case (.publisher, .publisher), (.run, .publisher), (.publisher, .run):
// 只要至少其中一个为 `publisher`,就返回串联后的 `publisher`。
// 因为 `Effect` 实现了 `Publisher` 协议,
// 所以直接调用自带的 `Publishers.Concatenate` 进行串联
return Self(
operation: .publisher(
Publishers.Concatenate(prefix: self, suffix: other).eraseToAnyPublisher()
)
)
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
/// 两个都是 `run`,返回合并后的 `run`
return Self(
operation: .run { send in
// 因为是串联,所以先执行左边,再执行右边。
// `cancellableValue` 是在 extension 中定义的 getter 类型的 async 属性,
// 所以调用 `await task.cancellableValue` 就可以保证任务执行完成后,
// 代码才会继续执行,这样就可以实现串联的效果
if let lhsPriority = lhsPriority {
await Task(priority: lhsPriority) { await lhsOperation(send) }.cancellableValue
} else {
await lhsOperation(send)
}
if let rhsPriority = rhsPriority {
await Task(priority: rhsPriority) { await rhsOperation(send) }.cancellableValue
} else {
await rhsOperation(send)
}
}
)
}
}
/// 把泛型 `Action` 转换成其他类型
@inlinable
public func map<T>(_ transform: @escaping (Action) -> T) -> Effect<T, Failure> {
switch self.operation {
case .none:
return .none
case let .publisher(publisher):
// 直接调用 `publisher.map` 转换
return .init(operation: .publisher(publisher.map(transform).eraseToAnyPublisher()))
case let .run(priority, operation):
return .init(
operation: .run(priority) { send in
// 在传入的 `Send` 参数内部转换
await operation(
Send { action in
send(transform(action))
}
)
}
)
}
}