[WWDC21] Meet AsyncSequence

WWDC21 영상보면서 이해한대로 대충 끄적여보기 첼린지

정확한 이해가 필요하신 분들은 영상을 직접 시청하는 것을 권합니다.

아마 내용이 많이 겹치겠지만, 실질적인 사용법 관련해서 더 많이 다룰 것 같은 느낌

0분 37초 - QuakesTool

@main
struct QuakesTool {
    static func main() async throws {
        let endpointURL = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")!

        // skip the header line and iterate each one 
        // to extract the magnitude, time, latitude and longitude
        for try await event in endpointURL.lines.dropFirst() {
            let values = event.split(separator: ",")
            let time = values[0]
            let latitude = values[1]
            let longitude = values[2]
            let magnitude = values[4]
            print("Magnitude \(magnitude) on \(time) at \(latitude) \(longitude)")
        }
    }
}

asyncSequence라는게 음.. async function을 roop로 돌렸을 때 처리하는 거에 대해서 다뤄지는 거겠죠? sequence라는게 또 Rx에서의 sequence를 보면 또 다른 느낌이라서..

지금 위 코드 예시를 보니 for문에 try await를 써서 async를 처리하는 모습을 보여주고 있네요!

이거 없을 땐 dispatchSemaphore 써서 기다렸는데 확실히 블럭도 줄고 더 간편하네요 👍

3분 24초 - Iterating a Sequence

for quake in quakes {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}

아마 비교 분석할려고 예시를 만든 것 같아요! 여기서 딱히 부연설명할 필요는 없는듯..

3분 52초 - How the compiler handles iteration

var iterator = quakes.makeIterator()
while let quake = iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}

이건 iterator를 만들어서 수행하는 건데, 다른 느낌이긴한데 이걸 비동기라고 생각해본 적 없는데… complier가 handling하는 게 어떤 차이가 있는지 모르겠네요..

4분 11초 - How the compiler handles asynchronous iteration

var iterator = quakes.makeAsyncIterator()
while let quake = await iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}

현재 새로나온 비동기 제어를 위해 await 키워드를 추가한 예시입니다. 음…

아마도 차이라고 치면 await가 await하는 작업을 suspend하는 거니까 complete call할 때까지 다른 작업하다가 complete call되면 다시 진행되는 것 같다.. 음.. 그러면 그냥 써도 되는걸 await를 붙여서 loop 자체도 async하게 돌릴 수 있다 뭐 그런건가..

4분 28초 - Iterating an AsyncSequence

for await quake in quakes {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}

이것도 뭐 위에랑 느낌만 다르지 똑같은 예시라고 생각이 듭니다.

7분 15초 - Concurrently iterating inside an async task

let iteration1 = async {
    for await quake in quakes {
        ...
    }
}

let iteration2 = async {
    do {
        for try await quake in quakeDownload {
            ...
        }
    } catch {
        ...
    }
}

//... later on  
iteration1.cancel()
iteration2.cancel()

이런 식으로 Task 단위로 만들어서 처리도 가능합니다… 어… 근데 좀 참신한거 없니.. 이거 다 봤던 내용인데..

7분 56초 - Reading bytes from a FileHandle

for try await line in FileHandle.standardInput.bytes.lines {
    ...
}

8분 16초 - Reading lines from a URL

let url = URL(fileURLWithPath: "/tmp/somefile.txt")
for try await line in url.lines {
    ...
}

8분 49초 - Reading bytes from a URLSession

let (bytes, response) = try await URLSession.shared.bytes(from: url)

guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200 /* OK */
else {
    throw MyNetworkingError.invalidServerResponse
}

for try await byte in bytes {
    ...
}

9분 12초 - Notifications

let center = NotificationCenter.default
let notification = await center.notifications(named: .NSPersistentStoreRemoteChange).first {
    $0.userInfo[NSStoreUUIDKey] == storeUUID
}

11분 10초 - Using an AsyncStream

let quakes = AsyncStream(Quake.self) { continuation in
	let monitor = QuakeMonitor()
	monitor.quakeHandler = { quake in
		continuation.yield(quake)
	}
	continuation.onTermination = { _ in
		monitor.stopMonitoring()
	}
	
	monitor.startMonitoring()
}

아.. 이건 예시 코드가 없어서 직접 썼다.. handler로 제어하는 코드에서 yield가 정확히 뭘 의미하는지 모르겠는데 아마도 값을 넘겨주는 거겠죠? 아무튼.. continuation을 통해서넘겨진게 결국 quakes로 다 담겨서 넘어오는거같습니다. onTermination은 아마… Task가 cancel될 때 호출되지 않을까요..

이것도 감이 아직 안오는데 써봐야 알듯..

Hello AsyncStream

  • AsyncSequence created with callbacks or delegates
  • Handles buffering
  • handles cancellation