
안녕하세요 !
이번에는 TCA에서의 비동기 처리 이해하기라는 주제로 작성해보려 합니다
TCA의 send 함수의 관계, 그리고 비동기 작업의 결과를 안전하게 상태에 반영하는 방법에 대해 살펴볼 예정입니다
func doAsyncBar() async {
var foo = ""
Task {
foo = "TASK"
}
}
이 코드는 컴파일 에러를 발생시킵니다 왜 그럴까요?
변수 foo를 새로운 스레드(Task)에서 수정하는 작업입니다
하지만 foo 값이 언제 변경될지는 불확실한데요
이러한 문제는 동시성 이슈를 야기할 수 있습니다
func doAsyncBar() async {
let foo = ""
Task {
print(foo)
}
}
이 방법은 컴파일 에러를 해결하지만, 값을 수정할 수 없어요 ㅜㅜㅜ
func doAsyncBar() async {
let foo = ""
let result = Task {
let task = "Task"
return task + foo
}
print(await result.value)
}
이 방법은 Task 내에서 값을 안전하게 사용하고 결과를 반환할 수 있게 해줍니다
var foo = "_Foo"
func doAsyncBar(_ foo: inout String) async {
let result = Task {
let task = "Task"
return task + foo
}
print(await result.value)
}
하지만 이 방법도 inout 파라미터를 비동기 context에서 사용할 수 없다는 에러를 발생시킵니다
TCA는 이러한 문제를 MainActor와 send 함수를 통해 해결합니다 (저번 포스팅에서 살명 드렸죠?)
var foo = "_Foo"
func doAsyncBar(
_ foo: inout String,
mainCompletion: @escaping @MainActor (String) -> Void
) async {
Task { [foo = foo] in
let taskResult = "Task" + foo
await mainCompletion(taskResult)
}
}
Task {
await doAsyncBar(&foo) { result in
print("Result :", result)
foo = result
print("Foo :", foo)
}
}
이 방식은 다음과 같은 이점을 제공합니다:
- 값을 안전하게 캡처하여 Task 내에서 사용할 수 있습니다
- 결과를 메인 스레드에서 처리할 수 있습니다
- 비동기 작업의 결과를 안전하게 상태에 반영할 수 있습니다
TCA는 이러한 패턴을 .run 메서드로 추상화하여 제공합니다:
case .requestButtonTapped:
return .run { send in
let fetchedImage = try await requestImages()
await send(.requestResponse(fetchedImage))
} catch: { error, send in
print(error)
}
case let .requestResponse(image):
state.image = image
return .none
TCA는 MainActor와 send 함수를 통해 비동기 작업의 결과를 안전하게 처리하고, 상태 업데이트를 메인 스레드에서 수행할 수 있게 해줍니다
이상으로 포스팅 마무리 하겠습니다.
.
.
.
감사합니다.