1. 기존의 비동기 함수

기존의 비동기 함수 정의 방법

  • 오래 걸려서 얻는 결과값을 반드시 콜백 클로저 형태로 돌려받아야 했다.
  • 올바르지 않은 형태

    // 기존 방식에서 잘못된 함수 설계의 형태
    func getImage1() -> UIImage? {
        var image: UIImage?
          
        /// 오래 걸리는 일(2번 cpu에게 일을 시킨다)
        DispatchQueue.global().async { 
            sleep(5)
            image = UIImage(systemName: "heart")
        }
        ///
        
        /// 1번 cpu에서 일을 한다
        return image 
        ///
    } // 기다리지 않고 image는 무조건 nil이 나옴.. 
    
  • 올바른 형태

    // 올바른 형태 (콜백함수 방식으로 설계해야 한다)
    func getImage2(callback: @escaping (UIImage?) -> Void) {
          
        // 오래 걸리는 일
        DispatchQueue.global().async {
            sleep(5)
            let image = UIImage(systemName: "heart")
            callback(image) // 콜백함수를 호출하여 데이터 전달
        }
    }
    

2. async/await라는 개념이 등장

  • getImage1()처럼 리턴을 하는 것도 가능하도록 설계 가능해졌다.

  • ⚠️비동기 작업을 수행하면서도 특정 작업의 순서를 보장하고 싶을 때 await을 사용

    // async: (이미지를 받아오는데) "오래걸리는 함수" 의미
    func getImage() async -> UIImage? {
        var image: UIImage?
        image = UIImage(systemName: "heart")
        return image
    }
      
    // async 함수는 호출시 await로 호출해야한다, Task 내에서 호출해야한다.
    // Task는 비동기 작업을 의미
    Task {
        let image1 = await getImage()
        let image2 = await getImage()
        let image3 = await getImage()
        let image4 = await getImage()
    }
    
  • 비동기 함수 안에서도 await 호출 가능하다. 이때는 Task 필요 x

    func doSomething() async {
        let image1 = await getImage()
        let image2 = await getImage()
        let image3 = await getImage()
        let image4 = await getImage()
    }
    
  • 기존 방식인 sleep -> Task.sleep 방식으로 변경
  • Sleep(5)는 해당 CPU가 일을 하지 못하게 만드는 코드였다.(Blocking 방식)
  • Task.sleep은 함수가 실행되다가 잠깐 멈췄다가 나중에 n초 뒤에 다시 실핼할 수 있다.(non-Blocking 방식)
    • 스레드가 비우게 만들어서 이 스레드에서 다른 일처리를 가능하게 해준다.
    • Await: 다시 실행되기를 기다리겠다
    // async: (이미지를 받아오는데) "오래걸리는 함수" 의미
    func getImage() async throws -> UIImage? {
        var image: UIImage?
        
        // sleep(5)
        // 기존 5초간 잠재우는 방식에서 변경
        try await Task.sleep(for: .seconds(5)) 
        // try가 들어간 에러를 던질 수 있는 함수이므로 함수 정의부분에 throws 추가(에러를 다시 밖으로 던질 수 있어야 한다)
        // 이유: 함수 내부에서 do-catch 문 처리를 하지 않아서 throws 키워드를 붙혀야 한다
        // 오래동안 걸리는 비동기 함수로 정의되어서 await도 추가..
        	
        image = UIImage(systemName: "heart")
        return image
    }
      
      
    // try? throws즉 에러를 던지지 않아도 된다
    // 에러 발생시 nil 리턴
    func getImage() async -> UIImage? {
       
        // 이를 실행하면 중단됬다가 5초뒤에 재개가능
        // 그동안 이를 사용하는 스레드는 다른 일처리가 가능해짐
        // 이미지가 생기는 시점에 다시 일처리가 재개
        try? await Task.sleep(for: .seconds(5)) 
        let image = UIImage(systemName: "heart")
        return image
    }
      
    // 비동기로 정의된 함수를 await로 호출해야한다는 의미는 (1)비동기는 함수가 오래 걸릴 수 있는 함수임을 의미하기도 하지만 (2)잠시 멈췄다가 다시 실행될 수 있는 함수이다.
    Task {
        let image = await getImage()
    }
    

Leave a comment