WWDC Notes: Meet AsyncSequence

Published on: June 9, 2021

Map, filter, reduce, dropFirst all work in async sequences:

for try await someThing in async.dropFirst() {
}

For example.

AsyncSequence suspends on each element and receives values asynchronously from the iterator. AsyncSequences either complete with success or stop when an error is thrown.

Implementing an AsyncSequence follows all the rules that a normal sequence follows. Its next() returns nil when it’s completed for example.

An async iterator also consumes its underlying collection.

Things like break and continue work in async sequences too.

You can cancel an iteration by holding on to its Task.Handle when you wrap it in async:

let handle = async {
  for await thing in list {
    // ...
  }
}

handle.cancel()

Reading files from a URL is commonly done async. You can use URL.lines for this. It works for network and local resources.

URLSession has a bytes(_:) function to enable roughly the same but with more control.

NotificationCenter notifications can be awaited too. You can even await one notification:

// listens for 1 notification only
let center = NotificationCenter.default
let notification = await center.notifications(named: .SomeNotification).first { notification in 
  print(notification)
}

Callbacks that are called multiple times, and some delegates are good candidates for async sequences.

Start / stop / handle pattern is a good candidate. Sounds similar to location managers.

let stream = AsyncStream(Output.self) { continuation in 
  let object = SomeObject()
  object.handler = { element in 
    continuation.yield(element)
  }

  object.onTermination = { _ in 
    object.stop()
  }

  // starts producing values
  object.start()
}

AsyncStream is the easiest way to create your own asynchronous sequences. They also handle buffering (not sure what that means in this case).

There’s also a throwing version: AsyncThrowingStream.

Note: AsyncStream does not appear to be present in Beta 1

Categories

WWDC21 Notes