DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
[WWDC21] Swift에서 async/await을 사용해보자(1)- (Meet async/await in Swift)
이 글은 예전 블로그의 글을 백업해온 것입니다.
이전 블로그 주소 ➡️ https://velog.io/@ldh0320

 

WWDC21
WWDC스터디 4-5주차
Meet async/await in Swift - WWDC21 - Videos - Apple Developer

 

📌 개요

기말고사가 끝나고 종강!! 그동안 쉬었던 WWDC 스터디를 다시 시작한다. 2주동안 시험 준비를 하고 다시 본업(?)에 집중하려고 한다. 지난 3번째 주제였던 ‘Meet async/await in Swift’를 마무리하고 내용을 정리하려고 한다. 지원서와 코테 준비, 또 이것저것 옮기느라 WWDC 스터디에 신경을 많이 못썼는데, 이제 더 집중해서 할 수 있겠다. 야호 ^)^

3번째 주제 ‘Meet async/await in Swift’는 태영님이 고른 주제로, 내가 고른 4번째 주제인 ‘Use async/await with URLSession’에 큰 도움이 될 것으로 기대된다!! 스터디 휴식기간 이전에 영상을 한번 봤는데, 1번만 봐서 내용 이해를 충분히 하지 못했다. 최근에 진행한 프로젝트 2개에서 모두 HTTP 통신이 이루어지는데, 그때마다 URLSession을 사용했고 거기서 또 비동기함수를 사용했다(!). 그래서 영상을 보면서 completion Handler 대신 async/await을 사용할 수 있다고 해서 더욱 관심이 가는 주제다.

그럼 고고릥.

 

 


 

 

1️⃣ Asynchronous programming

 

비동기 프로그래밍?

  • 자주 쓰인다!
  • 그러나 너무 장황하고(verbose), 복잡하고(complex), 부정확한(incorrect) 비동기 코드를 작성하기 쉽다.

 

그래서 이때 async/awailt이 도움이 될 수 있다.
깔끔한 비동기 코드를 쓰려면 async/await을 사용하자!

 

 

  • 일반 코드를 짜는 것처럼 쉽게 쓸 수 있다. (가독성이 좋고 깔끔해진다)
  • 코드에 아이디어가 잘 반영된다. (결국 코드에 논리가 잘 보인다는 소리)
  • 안전하다!

 

동기 vs 비동기

SDK에는 awaitable한 메소드가 많다.

async/await 말고도 비동기적 코드를 쓰도록 도와주는 메소드는 여러가지라는 뜻.

 

ex) UIkit의 UImage로 썸네일(이미지)를 생성할 수 있는 기능 제공

예를 들어 미리보기 화면인 썸네일 기능을 만들어야 할 때, 동기/비동기 방식 2가지로 모두 구현할 수 있다❗️

 

 

동기 방식

`fetchThumbnail`함수가 `prepareThumbnail`(UIKit이 제공하는 동기 함수)을 호출하면 `prepareThumbnail`의 작업이 완료될 때 까지 스레드가 다른 작업을 수행할 수 없다.

 

 

비동기 방식

completionhandler를 호출하여 prepareThumbnail 함수의 작업이 완료되었음을 알려준다!

prepareThumbnail 함수의 비동기 버전인 prepareThumbnail(of:completionHandler:)을 호출하면, 실행되는 동안 스레드가 자유롭게 다른 작업을 수행할 수 있습니다.

 

 

비동기 함수가 작업을 완료했을 때 이를 알려주는 방법에는...

  • completion handler
  • delegate callbacks
    등등..

 

 

2️⃣ Fetching a thumbnail

 

썸네일을 구현해보자.

그럼 화면에 썸네일 이미지를 불러오는 화면을 구현한다고 하자!

바로 위 사진처럼 구현하려고 한다.

 

 

썸네일 이미지를 다운로드하는 과정은 크게 4가지 작업으로 나눌 수 있다~

 

  1. thumbnailURLRequest
  2. dataTask(with:completion:)
  3. UIImage(data:)
  4. prepareThumbnail(of:completionHandler:)

 

 

4가지 작업 중에서 시간이 많이 소요되는, " expensive "한 작업들이 있다.

 

  • dataTask(with:completion:)
  • prepareThumbnail(of:completionHandler:)

 

동기 함수로 썸네일을 구현하면 시간이 많이 걸리는 작업들을 모두 기다려야 한다.

대신 비동기 함수를 사용하면... 기다리지 않고 스레드는 다른 작업을 처리할 수 있다!!

 

 

썸네일 함수 - 코드 💻

아래는 썸네일을 불러오는 함수 fetchThumbnail 이다.
이 함수는 위에서 말한 크게 4가지 과정으로 처리된다

  1. thumbnailURLRequest
  2. dataTask(with:completion:)
  3. UIImage(data:)
  4. prepareThumbnail(of:completionHandler:)

 

 

코드 

func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    // (1)
    let request = thumbnailURLRequest(for: id)
    // (2)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        // (2)-1
        if let error = error {
            completion(nil, error)

        // (2)-2
        } else if (response as? HTTPResponse)?.statusCode != 200 {
            completion(nil, error)

        // (3)
        } else {
            guard let image = UIImage(data: data!) else { // ❗️
                return
            }

            // (4)
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
                guard let thumbnail = thumbnail else { // ❗️
                    return
                }
                completion(thumbnail, nil)
            }
        }

    }
    task.resume()

}

fetchThumbnail이 호출되면…

      1. (1) : thumbnailURLRequest 먼저 호출
        • 이 메소드는 동기식이므로, completion handler가 필요없다...
      2. (2) : shared URLSession(URLSession.shared) 인스턴스에서 dataTask를 호출하여 해당 URLRequestcompletion handler 전달
        • 비동기 작업을 위해서 URLSessionDataTask를 동기적으로 생성한다!
        • 생성이 되고 나면, fetchThumbnail이 반환되고 스레드는 다른 작업을 수행할 수 있다.

 

📌 왜 dataTask를 비동기로 처리하나요?

더보기

이미지를 다운로드 하는 데 어느 정도 시간이 걸리는데, 그동안 스레드를 차단하고 싶지 않기 때문이다.

스레드를 차단하면 다른 작업 수행이 안된다. 이미지를 가져오는 동안 사용자가 다른 일을 처리할 수 있도록 해야 사용자가 앱을 느리다고 생각하지 않고 사용할 수 있다.

나도 어플을 사용할 때 로딩을 기다리는 것이 가장 힘들다🫣

즉, dataTask는 자기 할일을 수행하고, 스레드에서는 해당 함수가 작업을 완료할 때까지 다른 작업을 수행한다.

 

    • (2)-1 : 이미지 다운로드에서 에러가 발생할 경우
      • completion handler를 호출하고 오류를 전달한다.
    • (2)-2 : 이미지 다운로드에서 통신에 문제가 생긴 경우
      • 상태코드 200번이 아니면 문제가 있다는 뜻.
      • completion handler를 호출하고 오류를 전달한다.
    • (3) : 에러도 없고, 통신도 잘 되었을 경우 == 문제 없다!
      • 정상적으로 이미지가 다운로드 되었다.
      • gurad 문으로 UIImageinitWithData를 사용하여 데이터에서 이미지를 만든다.
        → 데이터 파일을 이미지로 변환해줌!
        → 이 과정을 UIImage 객체를 생성할 때 data 파라미터로 전달해주면 된당.
  1. (4) : 이미지가 생성되면 마지막으로 UIKitprepareThumbnail 메소드를 호출하고 completion handler 전달
    • 작업이 완료되는 동안 스레드는 차단 해제되고 다른 작업을 수행할 수 있다.
    • 썸네일이 준비된 다음,
      • 썸네일 준비 성공 !!!
        → 이미지와 함께 completion handler가 호출된다.
        (completion의 thumbnail 자리에 이미지가 들어감, error 자리엔 nil 그대로)
      • 썸네일 준비 실패...
        nil이 호출된다.
        (completion의 thumbnail 자리에 nil이 들어감, error 자리엔 nil 그대로)

그러나… 위의 코드에는 문제가 있다!

 

 

기존 코드가 가지는 문제점

func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    // (1)
    let request = thumbnailURLRequest(for: id)
    // (2)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        // (2)-1
        if let error = error {
            completion(nil, error)
        // (2)-2
        } else if (response as? HTTPResponse)?.statusCode != 200 {
            completion(nil, error)
        // (3)
        } else {
            guard let image = UIImage(data: data!) else {
                // (4)-1
                completion(nil, FetchError.badImage)
                return
            }
            // (4)
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
                guard let thumbnail = thumbnail else {
                    // (4)-1
                    completion(nil, FetchError.badImage)
                    return
                }
                completion(thumbnail, nil)
            }
        }

    }
    task.resume()

}

기존의 코드에는 (3)과 (4)의 guard 문에 completion 을 호출하지 않고 있다는 점을 염두하고 새로 작성한 위의 코드를 살펴보자.

    • (4)-1 : completion hanlder 호출
      • 만약 여기서 데이터를 UImage로 만드는 것에 실패하거나[(2)-3],
      • 이미지 축소에 실패하면[(3)]
        이 부분을 호출한 호출자에게 알림이 전송되지 않고, 행이 업데이트 되지 않는다(!!!)

completion handler를 호출하지 않은 경우,
guard문에서 문제가 발생하면 호출부에서는 이를 알 방법이 없다는 뜻.
그리고 이미지도 업데이트가 되지 않고 그대로 남아있게 된다.

  • So… fetchThumbnail을 작성한 우리(!)는 무슨일이 있어도 이를 호출자(fetchThumbnail)에게 알려줘야 한다.
  • 함수를 통한 모든 경로(every path through the function)는 호출자(fetchThumbnail)에게 알려줘야 한다.

그렇게 하기 위해서 completion handler를 호출하는 것. 이렇게 오류가 발생했다는 것을 알려줘야 한다.

 

swift는 함수의 실행이 어떻게 진행되는지 상관없이, 값이 return되지 않으면 오류가 발생하도록 되어 있는데,
기존 코드는 이 error handling 메커니즘이 사용이 안되고 있다.

 

문제가 발생하면 completion handler 내부에서 오류를... throw할수가 없다. (??이해가 잘 안된다)

swift에게서 fetchThumbnails 메소드와 같은 completion handlerclosure에 불과하기 때문임. (뭔소리야)

그래서 강제로 호출되도록…?? 하고 싶지만 이를 강제할 방법이 없다. 그래서 guard문에서 return 해도 컴파일 오류가 안생긴다는 말이다.

 

 

completion handler가 호출되는지 확인하는 것은 결국 사용자에게 달려있다.

이게 바로 completion handler가 가지는 문제점이다.

 

 

3️⃣ async/await

위 문제를 해결하는 방법에는 여러가지가 있는데.. 대표적으로 아래와 같다.

  1. result type
    • 더 안전하지만, 코드가 더 못생겨지고 길어짐
    • 이것 말고도 위 문제를 해결하는 방법이 여러개 있지만… 간단하고 쉽고 안전한 코드가 아님
    • 이때 이를 충족하는 것이..!
  2. async/await
    • 이것을 사용하면 비동기 코드를 더욱 간단하고, 쉽고, 안전하게 짤 수 있다.
    • 즉, 비동기 함수 수행 중 문제가 발생하였을 때 error를 바로 리턴할 수 있도록 해준다.
      이는 사용자가 completion handler로 에러가 발생될 수 있는 부분에 일일히 작성해주지 않아도 된다는 뜻이다.

 

async/await 을 사용해보자.

func fetchThumbnail(for id: String) async throws -> UIImage {
    // (1)
    let request = thumbnailURLRequest(for: id)
    // (2)
    let (data, response) = try await URLSession.shared.data(for: request)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
    // (3)
    let maybeImage = UIImage(data: data)
    // (4)
    guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
    return thumbnail
}

썸네일을 다운로드하는 4가지 절차를 위의 코드로 다시 작성할 수 있다.

앞의 코드에서는 fetchThumbnail의 매개변수로 문자열과 completion hanlder도 전달되었지만…
이번 코드에서는 함수에 async 키워드를 붙임으로써 함수 자체가 비동기식으로 작성되었다.

 

 

함수를 비동기로 표시할 때 키워드

  • 함수 시그니처에서 throws의 앞에 async
  • 혹은 화살표 앞에 async (함수가 throw를 하지 않는 경우)

async로 표시하면 함수와 함수의 특징이 더욱 간단해진다.

  • 이미지 다운로드에
    성공하면(다운로드되어서 원하는 크기로 줄어든 경우) → 해당 썸네일이 return.
    오류 발생하면 → 그냥 thrown! 오류를 return. (오류를 던져준다~)
    1. (1) : fetchThumbnail이 호출되면 이전과 마찬가지로 thumbnailURLRequest를 호출하여 시작한다.
      • 이 함수는 동기식이므로 스레드가 차단되어 작업을 수행한다.
    2. (2) : 다음으로 URLSession.shared에서 data(for: request)를 호출하여 데이터 다운로드를 시작한다.
      • dataTask와 마찬가지로 이 메소드도 Foundation에서 제공하며 비동기식이다.
      • 그러나 dataTask와 달리 data 메소드는 awaitable 이다.
      • 따라서 호출된 후 스레드를 차단 해제하여 빠르게 일시 중단된다.
        → 그러면 스레드는 다른 작업을 자유롭게 수행할 수 있다.

 

📌 dataTask와 data의 차이?

더보기

 

async/await 지원 여부에 따라 달라진다. (함수에 async 선언이 포함되어 있는가 없는가 차이)

async/await은 작년에 추가되었기 때문에 기존 함수에 사용되고 있지 않다.
dataTaskcompletion handler를 사용하고 있어서 이 함수는 async/await 구문에서 사용하지 못한다.
그래서 async 를 넣은 data 메소드를 새로 만들어서 사용하는 것이다.

 

📌 정리

dataTask 메소드 → async/await 구문에서 사용하지 못함.
data 메소드 → data 계열 함수에는 async가 있으므로 async/await 구문에서 쓸 수 있다고 이해하면 된다.

  1. data 메소드가 throws로 표시되어 있기 때문에 try가 해당 코드에 있다.
  2. 또한 이전 버전에서는 오류를 확인하고 명시적으로 completion handler를 호출해야 했지만, 위 버전(awaitable version)에서는 모두 try 키워드로 요약할 수 있다. ^ㅂ^)
    • throw로 표시된 함수를 호출하려면 try가 필요한 것 처럼, async로 표시된 함수를 호출하려면 await가 필요하다.
    • 표현식에 여러 비동기 함수 호출이 있는 경우... 여러 throw 함수 호출이 있는 표현식에 대해 try가 하나만 필요한 것 처럼 await를 한번만 작성하면 됩니다.
    • 결국 함수 호출은 try await .(를 붙여라!!)
  • throws async expression을 다룰 때는 다음과 같이 await전에 try를 넣어야한다.

 

💡 데이터가 다운로드 되면… 데이터 메소드가 다시 시작되어 fetchThumbnail 로 돌아간다.


그 시점에서 메소드가 return할 값 혹은 throw할 오류값이 들어온다.
만약 오류가 발생하면 fetchThumbnail이 차례로 오류를 발생시키고,
아니면 코드 그대로 쭉쭉 흘러간다.(영상에서는 데이터 및 응답 변수가 정의된다고 말함)

→ 이는 이전 버전의 fetchThumbnail에서 URLSessiondataTask메소드에 전달된 completion handler가 호출되었을 때 발생한 것과 유사함!

 

둘 다 비동기 메소드에서 발생하는 값, 오류들이 들어오고 처리를 하긴 하지만awaitable한 버전(=async/await)이 더 간단하다.


 

여기서 계속 같은 말을 하고 있는데, 결국 async/await을 쓰면 completion handler를 사용했을 때 보다 더 간단하게 오류를 잡아내고 비동기 코드를 쓸 수 있다는 거다.

오류를 잡기 위해 try만 써두면 사용자가 따로 completion handler를 호출할 필요가 없다는 거지. 결국 사용자의 실수를 코드 상에서 바로 잡을 수가 있다는 것 == 빈틈을 더욱 쉽고 간편하게 매울 수 있다~

결국… 이게 전부다. 코드는 20줄에서 6줄로 요약이 가능해버린다. wowowow

요청을 하고 반환된 값을 변수에 할당하여 사용할 수 있게 한다. 그리고 문제가 발생하면? 오류를 던져준다(throws).

지금까지 앱에 썸네일을 다운로드하는 방법을 4가지 작업으로 진행하고 있다.

 

 

잠깐, 여기서 thumbnail 함수는 뭔데?

왜 갑자기 나오는건데 왜!!!

→ 이는 SDK에서 제공해주는 건 아니고, 로버트씨가 추가했다고 하네요 ^_^)

 

근데 왜 thumbnail 함수에 await이 붙어있을까?
thumbnail 함수가 바로 ‘비.동.기' 이기 때문이다! 

 

 

📌 혹시 '함수'만 비동기식으로 만들 수 있나요?

더보기

아니요. 함수가 아니더라도 비동기식일 수 있습니다. (Not just functions can be async.)
Properties, initializers 들도 가능합니다.

 

 

thumbnail 함수 코드

extension UIImage {
    var thumbnail: UIImage? {
        get async {
            // (1)
            let size = CGSize(width: 40, height: 40)
            // (2)
            return await self.byPrepareingThumbnail(ofSize: size)
        }
    }
}

UIImage의 extension에서 이 property를 정의했고, 매우 짧다!

  1. (1) : CGSize를 형성하고
  2. (2) : byPrepareingThumbnail(ofSize)에 전달한 결과를 기다립니다.

 

주의할 점?

    1. 명시적 getter
      • 이것은 property를 비동기로 표시하는 데 필요합니다.
      • Swift 5.5 부터, property gettersthrow 할 수 있습니다.
      • 그리고 비동기 함수 시그니처와 마찬가지로 property가 비동기이면서 throw인 경우 async 키워드는 throw 전에 나타납니다.
    2. propery에는 setter가 없다.
      • read-only properties만 비동기식일 수 있습니다.
      • 이유 : functions, properties, initializers 에서 함수가 스레드를 차단 해제할 수 있는 위치를 나타내기 위해 표현식에서 await를 사용할 수 있습니다.

 

 

📌 await를 사용할 수 있는 또 다른 장소 → for loops!

더보기

비동기 시퀀스는 요소를 비동기적으로 vend(팔다, 의견이나 생각을 발언하다) 한다는 점을 제외하고는 일반 시퀀스와 같습니다.
(An async sequence is just like a normal sequence except that it vends its elements asynchronously.)

그래서 다음 항목(next item)을 가져오는 것은 비동기임을 나타내는 await 키워드로 표시되어야 합니다.
(So fetching the next item must be marked with the await keyword, indicating that it’s async.)

함수가 비동기 시퀀스를 반복하면서 다음 요소를 기다리는 동안, 스레드의 차단을 해제하고 루프의 본문에 다음요소를 사용하거나 요소가 남아있지 않은 경우 루프 뒤에 다시 시작할 수 있습니다.

 

💡 AsyncSequence에 대해 자세히 알아보려면 MeetAsyncSequence 세션 참고!
💡 많은 비동기 작업을 병렬로 실행하는 데 관심이 있다면 Structured concurrency in Swift 세션 참고!

 

그래서! await을 쓰는 곳은 정말 많다.

 

 

4️⃣ what happens when you call a function

지금까지 쓴 키워드들은 비동기 함수가 해당 부분에서 일시 중단될 수 있음을 나타낸다.

그렇다면 비동기 함수가 일시 중단된다는 것은 무엇을 의미하나?

→ 이에 대한 답을 얻기 위해 함수를 호출할 때 어떤 일이 발생하는지 생각해보자.

 

A normal function call

func thumbnailURLRequest(for id: String) -> URLRequest {
    // ...
    return request
}

fetchThumbnail 함수가 thumbnailURLRequest 함수를 호출하면, 실행 중인 스레드의 제어를 thumbnailURLRequest 함수에 넘긴다.

  • 만약 thumbnailURLRequest 함수가 일반적인 함수(normal function)이면, 스레드는 이 함수의 작업이 완료될 때까지 완전히 사용된다(fully occupied == 계속 쓰레드를 점유하고 있다)!!
    → 쓰레드는 thumbnailURLRequest 함수의 작업에 사용되므로 다른 작업이 수행될 수 없다.
  • thumbnailURLRequest 함수의 위치는 함수 자체 본문에 있을 수도 있고, 다른 함수에 있을 수도 있다.

어쨌든, thumbnailURLRequest 함수가 값을 반환하거나 오류를 발생시켜서 작업이 완료가 되면 fetchThumbnail 함수에 다시 제어권을 넘겨준다. (쓰레드를 fetchThumbnail 함수가 다시 점유)

 

 

💡 일반 함수가 쓰레드 제어를 포기하는 유일한 방법: by finishing
→ 함수의 작업이 '끝나야'한다.

 

An asynchronous functin call

async 키워드가 붙은 함수는 상황이 조금 다르다!

물론 일반 함수처럼 수행이 완료가 되면 함수에 대한 제어가 반환된다. 그러나…

 

💡 async 키워드가 붙은 함수가 쓰레드 제어를 포기하는 방법: by suspending
→ 함수를 ‘일시 중단'한다.

일반 함수와 마찬가지로… fetchThumbnail 함수가 비동기 함수 data를 호출하면 쓰레드에 대한 제어 권한을 data 함수에 준다.비동기 함수가 실행되면 ‘일시 중단' 될 수 있다.

  • 이때 data 함수는 쓰레드에 대한 점유(제어)를 포기한다.
  • BUT! fetchThumbnail 함수에 쓰레드 제어권(control)을 다시 주는게 아니라, System에 제어권을 제공한다!
    → 이러면 fetchThumbnail 함수도 ‘일시 중단'이 된다.
  • ‘일시 중단'은 시스템에게 “시스템 너 할일 많은거 내가 알아(I know you have a lot of work to do)”라고 말하는 방법이다.(비동기로 함수를 일시정지 해두면 그동안 쓰레드를 이용하여 다른 작업을 처리할 수 있으니까)
    → 결국 어떤 작업이 더 중요한지 결정하는 거다.
  • 그렇게 실행하다 보면… 다시 일시중단했던 함수를 재개해야 할 때가 온다.

시스템이 일시 중단한 data 함수를 재개(resume)하면 비동기 함수 data 는 쓰레드를 다시 제어하고 작업을 계속해 나갈 수 있다.

  • 당연히 또 원하면 일시 중단할 수 도 있다.
  • 비동기 함수는 일시 중단될 수 있지만, async로 표시되어 있다고 해서 반드시 일시중지 되는 것은 아니며, await가 표시된다고 해서 함수가 확실히 그곳에서 일시 중단되는 것도 아니다.

그러나 결국, 일시 중단하지 않거나 마지막으로 다시 시작한 후에, 반환값 또는 오류와 함께 스레드에 대한 제어 권한을 다시 fetchThumbnail 함수로 넘겨주면서 함수가 종료된다!

 

 

함수가 일시 중단될 때 어떤 일이 발생할 수 있을까?

fetchThumbnail을 다시 살펴보자.

func fetchThumbnail(for id: String) async throws -> UIImage {
    // (1)
    let request = thumbnailURLRequest(for: id)
    // (2)
    let (data, response) = try await URLSession.shared.data(for: request)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
    // (3)
    let maybeImage = UIImage(data: data)
    // (4)
    guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
    return thumbnail
}

fetchThumbnailURLSession의 비동기 data 메소드를 호출하면 data 메소드는 비동기 함수만 할 수 있는 특별한 방식(일시 중단)으로 스레드에서 실행을 중지합니다.

 

그러나 이 시점에서는 시스템이 통제되고 있으며, 해당 작업이 즉시 시작되지 않을 수도 있습니다.

대신 쓰레드는 다른 용도로 사용될 수 있다! → 다른 작업이 진행될 수 있습니다.

 

 

 

예) fetchThumbnail이 호출된 후 사용자가 데이터를 업로드하는 버튼을 탭한다고 가정

사용자가 게시물에 반응을 했다고 해보자. 이때 게시물에 대한 반응으로 좋아요 버튼(== 데이터를 업로드 하는 버튼)을 누른다고 하자!

 

그럼 시스템은 이전에 대기 중인 작업 전에 사용자의 반응을 게시하는 작업(좋아요 반영)을 자유롭게 실행할 수 있다.

 

그리고 그 ‘좋아요' 작업이 완료가 되면… URLSession의 data 함수(비동기)가 재개(resume) 될 수 있다!
혹은 시스템이 또 다른 작업을 실행할 수도 있는 거공~

 

그렇게 data 메소드가 완료되면, fetchThumbnail 함수로 다시 돌아간다…
→ 이런 식으로 쓰레드가 사용될 수 있당

 

💡 함수가 일시 중단되는 동안 다른 작업이 수행될 수 있다는 사실때문에
swiftawait 키워드로 async 호출을 표시해야한다고 주장함!!!

 

async/await에 대해 기억해야 할 몇 가지 중요한 사항

  1. 함수를 async로 표시하면 일시 중단(to suspend)하도록 허용한다.
    • 함수가 자신을 일시 중단하면, 그 함수를 호출한 호출자도 일시 중단이 된다.
      → 호출자도 비동기식(async)이어야 한다.
  2. 비동기 함수(async function)에서 한번 또는 여러 번 일시 중단될 수 있는 위치를 가리키기 위해(point out) await 키워드가 사용된다.
  3. 비동기 함수가 일시 중단되는 동안 쓰레드는 차단되지 않는다(be not blocked).
    • 따라서 시스템은 다른 작업을 자유롭게 예약할 수 있다.
    • 또한 나중에 시작되는 작업도 먼저 실행할 수 있다!!
      → 즉, 함수가 일시 중단되는 동안 앱의 state(상태)가 크게 변화할 수 있습니당.(썸네일을 불러오는 동안 사용자가 다른 버튼을 클릭한다던지?)
  4. 비동기 함수가 다시 재개(resume)될 때, 비동기 함수에서 반환된 결과(반환값 혹은 오류)는 원래의 함수(호출자)에 돌아가고, 실행(excution)은 일시 중단되었던 바로 그 부분에서 계속된다.

 


 

여기까지 Swift에서 async/await이 어떻게 작동되는지에 대한 세션이었다. 다음은 이것을 프로젝트에서 사용하는 방법을 알려주는 내용이다. (2편에서 이어집니다 @_@)

  Comments,     Trackbacks