// Example1
func fetchItems() async { }
// Example2
func fetchSomeItems() async throws -> String { }
await fetchItems()
await fetchSomeItems()
Async 작업의 단위입니다.
스위프트에서 DispatchQueue 나 OperationQueue, Thread 따로 없이 작성하면 main thread 인 sync(=동기)로 동작합니다. 이때, Task 블록으로 감싸서 async 코드를 수행할 수 있도록 제공해줍니다.
Task 사용 예시
Task {
await fetchItems()
}
// 물론, Task 를 사용하지 않고도 Async 함수를 사용할 수 있습니다.
// 만약 Task 블록 없이 async 함수를 호출하려면, async 함수를 호출하는 쪽도 async여야 가능합니다.
// viewDidLoad()는 override 함수라, async를 붙이지 못하지만 이론상 async를 붙이면 Task 블록 없이도 호출 가능합니다.
// Task 를 사용하지 않은 Async 사용 예시
override func viewDidLoad() async {
super.viewDidLoad()
await fetchItems()
}
override func viewDidLoad() {
super.viewDidLoad()
print("1. will enter task block")
Task {
print("2. did enter task block")
// await를 만남 -> 다음 라인의 코드 실행시키지 않고 대기
try? await Task.sleep(for: .seconds(10))
print("3. will out task block")
}
print("4. some another code")
}
/*
1. will enter task block
4. some another code
2. did enter task block
3. will out task block
1 - 4 - 2 - 3 순으로 실행됨을 확인할 수 있습니다.
*/
override func viewDidLoad() {
super.viewDidLoad()
Task {
try? await Task.sleep(for: .seconds(10))
// await를 만남 -> 다음 라인의 코드 실행시키지 않고 대기함
}
}
override func viewDidLoad() {
super.viewDidLoad()
// DispatchQueue 는 해당 Thread 가 점유하고 일을 하고 있으면, 그 스레드는 Block 되어 해당 일을 끝낼때까지 다른 일 처리를 못하는 상태입니다.
DispatchQueue.global().async {
self.fetchHeavyItems()
}
}
func fetchHeavyItems() {
// get some heavy items
}
Await 로 데이터를 얻지 않고, Async 로 프로퍼티를 선언하여, 동시성 처리에 사용
Async let 사용 예시
// Async let 선언 방법
async let a = getA()
func getA() async -> Int {
try? await Task.sleep(for: .seconds(3))
return 3
}
func getB() async -> Int {
try? await Task.sleep(for: .seconds(5))
return 5
}
// getA, getB 두 개 모두 동시에 실행가능하지만, getA에서 3초 대기 후 getB에서 5초 대기하여 실행, 최종 8초간 대기하는 비효율적인 코드
Task {
let a = await getA()
let b = await getB()
print(a + b)
}
// 최종 : 8초
Task {
async let a = getA()
async let b = getB()
let sum = await (a + b)
// 비동기 바인딩
print(sum)
}
// actor 사용 방법
actor Counter {
private let name: String
private var count = 0
init(name: String) {
self.name = name
}
func plus() {
count += 1
}
}
let counter = Counter(name: "counter")
actor Counter {
private name: String
private var count = 0
init(name: String) {
self.name = name
}
func plus() {
count += 1
}
}
일반적인 인스턴스 호출하듯이 actor 의 메소드를 호출할 수 없습니다.
Actor 의 메소드 호출 예시
Task, Await 키워드를 사용하면 됩니다.
let counter = Counter(name: "counter")
Task {
await counter.plus()
}
let 키워드로 선언한 actor 는 읽기만 가능합니다. 또한, Race Condition 이 발생하지 않는 것을 보장합니다.
메소드 앞에 Nonisolated 를 붙여서 사용하면, Task, Await 키워드 없이 사용할 수 있습니다.
사용 예시
// nonisolated 키워드 예시
actor Counter {
private let name: String
private var count = 0
init(name: String) {
self.name = name
}
func plus() {
count += 1
}
nonisolated func getName() -> String { // <-
name
}
}
// 사용하는쪽
let counter = Counter(name: "counter")
counter.getName() // Task, await 키워드 없이 접근 가능
항상 main 에서 동작하는 actor 를 의미합니다.
항상 메인 스레드에서 실행되어야 하는 클래스입니다.
@MainActor 를 사용하여 구현합니다.
사용 예시
@MainActor
class SomeClass { }
class SomeClass {
@MainActor
func runSomeMethod() {
// Do stuff
}
}
struct AlbumResult: Codable {
let results: [Album]
}
struct Album: Codable, Hashable {
let collectionId: Int
let collectionName: String
let collectionPrice: Double
}
enum APIError: Error {
case invalidURL
case noData
}
enum API {
static func fetchAlbums(completion: @escaping (Result<AlbumResult, Error>) -> Void) {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
completion(.failure(APIError.invalidURL))
return
}
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(APIError.noData))
return
}
do {
let result = try JSONDecoder().decode(AlbumResult.self, from: data)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
.resume()
}
}
API.fetchAlbums { result in
switch result {
case let .success(response):
print(response)
case let .failure(error):
print(error)
}
}
URLSession 앞에 Await 키워드를 붙여 사용하면 됩니다.
사용 예시
// MARK: Async & Await
static func fetchAlbums() async throws -> AlbumResult {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
throw APIError.invalidURL
}
let (data, _) = try await URLSession.shared.data(from: url)
let result = try JSONDecoder().decode(AlbumResult.self, from: data)
return result
}
// async await
Task {
do {
let result = try await API.fetchAlbums()
print(result)
} catch {
print(error)
}
}