0.53.0よりtaskとfireAndForgetはSoft-Deprecatedとなりました
今後はrunのみを使いましょう
Reducerで非同期処理を実行するためのツールとして、EffectTaskには3つのstaticメソッドが用意されています。
使い分けのポイントとしては以下のようになります。
メソッド | 用途 |
---|---|
task(priority:operation:catch:) |
1つのActionをSendする |
run(priority:operation:catch:) |
0または1つ以上のActionをSendする |
fireAndforget(priority:_:) |
ActionをSendしない |
task(priority:operation:catch:)
task
では、以下のようにActionをreturnする必要があります。
returnする前に、複数の非同期処理を実行可能です。
return .task {
try await operation1()
try await operation2()
return await .fetchResponse(TaskResult {
try await self.fetch()
})
}
run(priority:operation:catch:)
run
の使いどころは主に2つあります。
一つは、for-await-inでAsyncSequenceを処理するケースです。
return .run { send in
for await newValue in self.listen() {
await send(.updated(newValue))
}
}
もう一つは、条件によってActionを送信しなかったり、あるいは複数送信するようなロジックを持つケースです。
return .run { send in
guard ... else { return } // 何もしない
await send(
.fetchResponse(TaskResult {
try await self.fetch()
})
)
// もっとActionをsendすることもできる
}
isowordsでは、withThrowingTaskGroup
を使って複数の非同期処理を並列実行させるような使い方もしていました。
fireAndforget(priority:_:)
非同期処理を実行するだけでActionを送信しない場合は、fireAndforget
を使います。
return .fireAndForget {
try await operation1()
try await operation2()
}
エラーのキャッチについて
task
とrun
はcatch
という引数にエラーを受け取るクロージャを指定できます。
operation
クロージャでエラーがスローされた場合、catch
クロージャが実行されます。
return .task {
try await operation() // ここでエラーがスローされるとする
return await .fetchResponse(TaskResult {
try await self.fetch()
})
} catch: { // ここでキャッチ
return .operationFailed // エラーが発生したことを通知するActionをSendする
}
あるいは、do-catchでエラーをキャッチします。
return .task {
do {
try await operation()
return await .fetchResponse(TaskResult {
try await self.fetch()
})
} catch {
return .operationFailed
}
}
task
およびrun
でエラーをキャッチしなかった場合、以下のようなワーニングが出力されます。
An "EffectTask.task" returned from "SomeFeature.swift:58" threw an unhandled error. …
SomeFeature.OperationError()
All non-cancellation errors must be explicitly handled via the "catch" parameter on "EffectTask.task", or via a "do" block.
なお、fireAndForget
はエラーがスローされても無視され、ワーニングも出力されません。