


Kotlin, Go, JavaScript, Python, Rust, C#, C ++ 등과 같은 많은 언어는 이미 Async/Await 패턴 구현을 가능하게하는 코 루틴 지원을 받았습니다. 이 기능은 아직 Swift에서 지원되지 않지만 언어를 변경할 필요없이 프레임 워크에 의해 개선 될 수 있습니다.
비동기 프로그래밍은 일반적으로 콜백과 관련이 있습니다. 너무 많아서 둥지가 너무 많을 때까지 매우 편리합니다. 그런 다음 파라미드의 파이마 또는 콜백 지옥 이라고합니다.
비동기식 프로그래밍의 또 다른 문제는 스위프트의 자연 오류 처리 메커니즘을 사용할 수 없기 때문에 오류 처리 입니다.
Combine, RXSwift, PromiseKit 등과 같은 비동기 코드를 쉽게 사용할 수있는 다른 많은 프레임 워크가 있습니다. 그들은 몇 가지 단점이있는 다른 접근법을 사용합니다.
비동기/대기 패턴은 비동기식 비 차단 함수가 일반 동기 기능과 유사한 방식으로 구조화 될 수있는 대안입니다.
이미 다른 프로그래밍 언어에서 잘 확립되어 있으며 비동기 프로그래밍의 진화입니다. 코 루틴 덕분 에이 패턴의 구현이 가능합니다.
Coroutine 내부의 예제를 살펴 보겠습니다.이 내부는 await() 가 스레드를 차단하지 않고 결과를 사용할 수있을 때 ()을 일시 중지하고 재개합니다.
//executes coroutine on the main thread
DispatchQueue . main . startCoroutine {
//extension that returns CoFuture<(data: Data, response: URLResponse)>
let dataFuture = URLSession . shared . dataTaskFuture ( for : imageURL )
//await CoFuture result that suspends coroutine and doesn't block the thread
let data : Data = try dataFuture . await ( ) . data
//create UIImage from the data
guard let image = UIImage ( data : data ) else { return }
//execute heavy task on global queue and await the result without blocking the thread
let thumbnail : UIImage = try DispatchQueue . global ( ) . await { image . makeThumbnail ( ) }
//set image in UIImageView on the main thread
self . imageView . image = thumbnail
}API 문서
코 루틴은 나중에 스레드를 차단하지 않고 일시 중단 및 재개 될 수있는 계산입니다. 코 루틴은 정기적 인 기능을 기반으로하며 실행 중에 전환 할 수있는 스케줄러에서 실행할 수 있습니다.
Coroutines API 디자인은 가능한 한 최소한입니다. 그것은 코 루틴 ( DispatchQueue )을 예약하는 방법 (이미 그것을 준수 함)과 유틸리티 방법으로 Coroutine 구조를 예약하는 방법을 설명하는 CoroutineScheduler 프로토콜로 구성됩니다. 이 API는 놀라운 일을하기에 충분합니다.
다음 예제는 코 루틴 내부의 await() 사용을 보여주기 위해 비동기 통화를 마무리합니다.
//execute coroutine on the main thread
DispatchQueue . main . startCoroutine {
//await URLSessionDataTask response without blocking the thread
let ( data , response , error ) = try Coroutine . await { callback in
URLSession . shared . dataTask ( with : url , completionHandler : callback ) . resume ( )
}
. . . use response on the main thread . . .
} 다음은 NSManagedObjectContext CoroutineScheduler 로 준수하여 코 루틴을 시작하는 방법입니다.
extension NSManagedObjectContext : CoroutineScheduler {
func scheduleTask ( _ task : @escaping ( ) -> Void ) {
perform ( task )
}
}
//execute coroutine on the main thread
DispatchQueue . main . startCoroutine {
let context : NSManagedObjectContext //context with privateQueueConcurrencyType
let request : NSFetchRequest < NSDictionary > //some complex request
//execute request on the context without blocking the main thread
let result : [ NSDictionary ] = try context . await { try context . fetch ( request ) }
}미래는 나중에 제공 될 결과를위한 읽기 전용 보유자이며 약속은이 결과의 제공자입니다. 이들은 비동기 작업의 최종 완료 또는 실패를 나타냅니다.
미래와 약속 접근 자체는 산업이되었습니다. 비동기 코드를 동기화하는 편리한 메커니즘입니다. 그러나 코 루틴과 함께 비동기 코드 사용을 다음 단계로 끌어 올리고 비동기/대기 패턴의 일부가되었습니다. 코 루틴이 골격이라면 미래와 약속은 근육입니다.
선물과 약속은 해당 공동 CoFuture 클래스와 CoPromise 서브 클래스로 표시됩니다.
//wraps some async func with CoFuture
func makeIntFuture ( ) -> CoFuture < Int > {
let promise = CoPromise < Int > ( )
someAsyncFunc { int in
promise . success ( int )
}
return promise
} 여러 작업을 병렬로 시작하고 나중에 await() 와 동기화 할 수 있습니다.
//create CoFuture<Int> that takes 2 sec. from the example above
let future1 : CoFuture < Int > = makeIntFuture ( )
//execute coroutine on the global queue and returns CoFuture<Int> with future result
let future2 : CoFuture < Int > = DispatchQueue . global ( ) . coroutineFuture {
try Coroutine . delay ( . seconds ( 3 ) ) //some work that takes 3 sec.
return 6
}
//execute coroutine on the main thread
DispatchQueue . main . startCoroutine {
let sum : Int = try future1 . await ( ) + future2 . await ( ) //will await for 3 sec.
self . label . text = " Sum is ( sum ) "
} CoFuture 를 새로운 것으로 변환하거나 구성하는 것은 매우 쉽습니다.
let array : [ CoFuture < Int > ]
//create new CoFuture<Int> with sum of future results
let sum = CoFuture { try array . reduce ( 0 ) { try $0 + $1 . await ( ) } }선물과 약속은 코 루틴 사이에 단일 값을 전달하는 편리한 방법을 제공합니다. 채널은 값 스트림을 전송하는 방법을 제공합니다. 개념적으로, 채널은 비어있는 경우 수신에 수신자를 일시 중단하거나 가득 차면 보내는 대기열과 유사합니다.
이 비 차단 원시는 Go 및 Kotlin과 같은 언어로 널리 사용되며 코 루틴 작업을 향상시키는 또 다른 도구입니다.
채널을 만들려면 CoChannel 클래스를 사용하십시오.
//create a channel with a buffer which can store only one element
let channel = CoChannel < Int > ( capacity : 1 )
DispatchQueue . global ( ) . startCoroutine {
for i in 0 ..< 100 {
//imitate some work
try Coroutine . delay ( . seconds ( 1 ) )
//sends a value to the channel and suspends coroutine if its buffer is full
try channel . awaitSend ( i )
}
//close channel when all values are sent
channel . close ( )
}
DispatchQueue . global ( ) . startCoroutine {
//receives values until closed and suspends a coroutine if it's empty
for i in channel . makeIterator ( ) {
print ( " Receive " , i )
}
print ( " Done " )
} 출시 된 모든 코 루틴, CoFuture S 및 CoChannel S는 일반적으로 참조 할 필요가 없습니다. 그들은 그들의 처형 후에 부정된다. 그러나 종종 더 이상 필요하지 않을 때 더 일찍 완료해야합니다. 이를 위해 CoFuture 와 CoChannel 취소 방법이 있습니다.
CoScope 이러한 물체의 수명주기를 더 쉽게 관리 할 수 있도록합니다. 그것은 당신이 약한 참조를 유지하고 필요한 경우 또는 Deinit에서 취소 할 수 있습니다.
더 이상 필요하지 않을 때 또는 Deinit에 있거나 Deinit에있을 때 CoScope 에 코 루틴, CoFuture S, CoChannel S 및 기타 CoCancellable 추가 할 수 있습니다.
class ViewController : UIViewController {
let scope = CoScope ( ) //will cancel all objects on `cancel()` or deinit
func performSomeWork ( ) {
//create new `CoChannel` and add to `CoScope`
let channel = makeSomeChannel ( ) . added ( to : scope )
//execute coroutine and add to `CoScope`
DispatchQueue . main . startCoroutine ( in : scope ) { [ weak self ] in
for item in channel . makeIterator ( ) {
try self ? . performSomeWork ( with : item )
}
}
}
func performSomeWork ( with item : Item ) throws {
//create new `CoFuture` and add to `CoScope`
let future = makeSomeFuture ( item ) . added ( to : scope )
let result = try future . await ( )
. . . do some work using result . . .
}
}