티스토리 뷰
[1] TaskGroup과 ThrowingTaskGroup
: A group that contains dynamically created child tasks. (자세한 설명은 이 글 5번 참고)
taskGroup을 만드려면
withTaskGroup(of:returning:body:) 를 호출.
: A group that contains throwing, dynamically created child tasks.
throwing task group을 만드려면
withThrowingTaskGroup(of:returning:body:) 를 호출
[2] 사용예제
2.1 간단한 병렬 실행을 위해 async-let syntax 를 쓰다가 확장이 필요할 때
여러장의 이미지를 병렬로 fetch 해와야하는 상황이라고 해봅시다.
저는 성공한 케이스들만 UIImage 배열로 리턴해줄 것 입니다.
이미지 fetch 메소드는 아래와 같고 (여기서 코드참고했어요)
private func fetchImage(urlString: String) async throws -> UIImage {
guard let url = URL(string: urlString) else {
throw URLError(.badURL)
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let image = UIImage(data: data) {
return image
} else {
throw URLError(.badURL)
}
} catch {
throw error
}
}
예를들어 2개의 연산 정도 되는 간단한 병렬 실행이면 async let 쓰겠지만..
func fetchImages() async -> [UIImage] {
async let fetchImage1 = fetchImage(urlString: "https://picsum.photos/300")
async let fetchImage2 = fetchImage(urlString: "https://picsum.photos/300")
let (image1, image2) = await (try? fetchImage1, try? fetchImage2)
return [image1, image2].compactMap { $0 }
}
이미지 여러개를 가져오도록 확장되었을 때는 async-let syntax 를 쓰기 힘들어집니다;;;
async let fetchImage1 = fetchImage(urlString: "https://picsum.photos/300")
async let fetchImage2 = fetchImage(urlString: "https://picsum.photos/300")
async let fetchImage3 = fetchImage(urlString: "https://picsum.photos/300")
async let fetchImage4 = fetchImage(urlString: "https://picsum.photos/300")
async let fetchImage5 = fetchImage(urlString: "https://picsum.photos/300")
let (image1, image2, image3, ...) = await (try? fetchImage1, try? fetchImage2, ...)
이 때 TaskGroup을 씁니다.
async-let syntax 도 내부적으로 child task 를 생성하는 데, 이와 동일하게 직접 child task를 생성하는 셈입니다.
에러를 던지지 않을 것이니까 withTaskGroup을 사용해줍니다.
메소드 시그니쳐가 좀 복잡한데 두개만 잘 체크해주면 됩니다.
- of 는 ChildTask 의 결과 타입
- body는 group -> GroupResult
func withTaskGroup<ChildTaskResult, GroupResult>(
of childTaskResultType: ChildTaskResult.Type,
returning returnType: GroupResult.Type = GroupResult.self,
body: (inout TaskGroup<ChildTaskResult>) async -> GroupResult
) async -> GroupResult where ChildTaskResult : Sendable
먼저 withTaskGroup 메소드로 TaskGroup을 만들고
addTask(priority:operation:) 로 group 에 childTask 를 추가합니다.
func fetchImages() async -> [UIImage] {
let urlStrings = [
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300"
]
let images = await withTaskGroup(of: UIImage?.self) { group in
for urlString in urlStrings {
group.addTask {
try? await self.fetchImage(urlString: urlString)
}
}
}
return []
}
이제 GroupResult (이미지 배열) 를 리턴해주는 코드를 추가합니다.
func fetchImages() async -> [UIImage] {
let urlStrings = [
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300"
]
let images = await withTaskGroup(of: UIImage?.self) { group in
for urlString in urlStrings {
group.addTask {
try? await self.fetchImage(urlString: urlString)
}
}
var images: [UIImage] = []
for await image in group {
if let image {
images.append(image)
}
}
return images
}
return images
}
참고로 TaskGroup은 AsyncSequence 를 conform 하고 있기 때문에
reduce 를 사용해서 간결하게 코드를 작성할 수도 있습니다.
func fetchImages() async -> [UIImage] {
let urlStrings = [
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300"
]
let images = await withTaskGroup(of: UIImage?.self) { group in
for urlString in urlStrings {
group.addTask {
try? await self.fetchImage(urlString: urlString)
}
}
return await group
.reduce(into: [UIImage?](), { $0.append($1) })
.compactMap { $0 }
}
return images
}
최종코드
func fetchImages() async -> [UIImage] {
let urlStrings = [
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300"
]
return await withTaskGroup(of: UIImage?.self) { group in
for urlString in urlStrings {
group.addTask {
try? await self.fetchImage(urlString: urlString)
}
}
return await group
.reduce(into: [UIImage?](), { $0.append($1) })
.compactMap { $0 }
}
}
To Be Continued...
addTask 때 TaskPriority 를 세팅하는 예제를 찾아보기!
'🍏 > Swift' 카테고리의 다른 글
[Swift] actor (0) | 2023.04.10 |
---|---|
[Swift] Sendable (0) | 2023.04.08 |
[Swift] Understanding Swift Performance 를 보면서 알게 된 것 (0) | 2023.03.04 |
[Swift Collections] deque (1) | 2022.10.08 |
[Swift] some, any 키워드 (2) | 2022.07.16 |
- Total
- Today
- Yesterday
- Watch App for iOS App vs Watch App
- Python Type Hint
- 장고 URL querystring
- drf custom error
- cocoapod
- 플러터 싱글톤
- github actions
- Dart Factory
- 플러터 얼럿
- flutter 앱 출시
- Sketch 누끼
- Flutter Spacer
- ipad multitasking
- Django Heroku Scheduler
- Flutter Text Gradient
- Django Firebase Cloud Messaging
- Flutter 로딩
- Flutter Clipboard
- DRF APIException
- 구글 Geocoding API
- flutter deep link
- flutter build mode
- ribs
- Django FCM
- flutter dynamic link
- 장고 Custom Management Command
- PencilKit
- Flutter getter setter
- SerializerMethodField
- METAL
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |