yyeonjju / Interview_Questions

0 stars 0 forks source link

Meet async/await in Swift (WWDC 2021) #20

Open yyeonjju opened 6 months ago

yyeonjju commented 6 months ago

https://developer.apple.com/videos/play/wwdc2021/10132/

Swift는 이제 일반적으로 async/await로 알려진 패턴인 비동기 함수를 지원합니다. 함수가 일시 중지되면 어떤 일이 발생하는지 알아보고 기존 완료 핸들러를 비동기 함수에 적용하는 방법을 알아보세요.

썸네일 이미지를 fetching 하는 과정에서 발생하는 비동기 작업

image

이 작업에 대해 completionHandler를 사용했을 때와 async/await를 사용했을 때를 비교해보자





⭐️ completionHandler 사용했을 때

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





func fetchThumbnail(for id: String, completion: @escaping (Result<UIImage, Error>) -> Void) {
    let request = thumbnailURLRequest(for: id)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            completion(.failure(error))
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(.failure(FetchError.badID))
        } else {
            guard let image = UIImage(data: data!) else {
                completion(.failure(FetchError.badImage))
                return
            }
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
                guard let thumbnail = thumbnail else {
                    completion(.failure(FetchError.badImage))
                    return
                }
                completion(.success(thumbnail))
            }
        }
    }
    task.resume()
}





⭐️ async/await 를 사용했을 때

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




URLSession의 data(for:) 메서드는 비동기 함수!!

image image




data(for:) 메서드 호출 시 앞에 try가 붙는 이유




data(for:)메서드의 작업(데이터 다운로딩)이 끝났으면, 다시 원래 작업했던 함수로 resume

image image image





completionHandler방식(dataTask 메서드)과 async-await방식(data 메서드) 모두 URLSession에 의해 error 와 value가 생성되지만! (방식의 차이) But the awaitable version is so much simpler.  후자(async-await)의 방식이 훨씬심플!






image image image





⭐️ async-await를 사용했을 때 completionHandler로 처리했던 비동기 함수와 다른점!








⭐️ 동기 함수와 비동기 함수의 실행 차이

비동기 함수를 실행했을 때 VS 동기 함수를 실행했을 때의 차이 를 보자면



내부 task 중, 동기 함수가 실행될 경우!!

image image





내부 task 중, 비동기 함수가 실행될 경우는 위와 완전히 다르다!!



suspend→ resume 과정






image image






image






image image






image





⭐️suspending 상태는 뭘 의미하고, await키워드가 필요한 이유




image image



==> 즉 함수가 일시중지되면 (중간에 많은 작업이 일어날 수도 있기 때문에) 앱의 상태가 크게 바뀔수도 있다는 점을 인지하고 있어야한다.









⭐️async-await 에서 주의해야할 점

  1. 함수를 비동기(async)로 표시하면 해당 함수가 일시 중지되는 것을 허용하는 것입니다. 그리고 함수가 자신을 일시 중지하면 호출자도 일시 중지됩니다. 따라서 호출자(caller)도 비동기여야 합니다. ( data(for:)라는 비동기 함수를 호출하는 함수 fetchThumbnail 함수도 비동기 )
  2. 비동기 함수에서 한 번 또는 여러 번 일시 중단될 수 있는 위치를 지적하기 위해 await 키워드가 사용된다.
  3. 비동기 기능이 일시 중단되는 동안 스레드는 ⭐️ block되지 않습니다⭐️. 따라서 시스템은 다른 작업을 자유롭게 스케줄링할 수 있습니다. 나중에 시작되는 작업이라도 먼저 실행할 수 있습니다. -> 이는 기능이 일시 중지된 동안 앱 상태가 크게 변경될 수 있음을 의미
  4. 마지막으로 비동기 함수(ex. data(for : ))가 resume되면 호출한 비동기 함수에서 반환된 결과가 원래 함수로 다시 유입되고 중단된 부분부터 실행이 계속된다.



yyeonjju commented 6 months ago

https://developer.apple.com/videos/play/wwdc2021/10132/?time=1189 More than that, the function may resume onto an entirely different thread.  이 부분은 아직도 이해 못하겠음.

https://tech.devsisters.com/posts/crunchy-concurrency-swift/ 여기서도 나오는 개념

yyeonjju commented 6 months ago

프로퍼티도 async 로 정의될 수있다.


byPreparingThumbnail() 함수의 과정에서 결과를 기다리며 suspend 한다 (await)

image image
ericKwon95 commented 6 months ago

https://developer.apple.com/videos/play/wwdc2021/10132/?time=1189 More than that, the function may resume onto an entirely different thread.  이 부분은 아직도 이해 못하겠음.

https://tech.devsisters.com/posts/crunchy-concurrency-swift/ 여기서도 나오는 개념

이 부분이 저도 헷갈려서 좀 더 찾아봤습니다! concurrency는 GCD와 다르게 비동기 작업이 늘어날 때 스레드를 새로 생성하지 않고 시스템에 제어권을 위임, 시스템이 우선순위에 따라 스레드에게 일을 할당하는 방식입니다. 이 때문에 스레드를 새로 생성하면 해당 스레드가 작업의 완료까지 책임지는 GCD와는 다르게, 2번 스레드에서 suspend 된 작업이 3번 스레드에서 resume 될 수도 있다는 것을 의미하는 것 같아요.

이미 보셨겠지만, 날진님 블로그에 이와 관련한 동작 정리가 잘 나와있더라구요!