Modern Async Primitives on iOS, Android, and the Web
Tags: async, swift, kotlin, javascript
Published: 2023-12-06 02:34:06

---
title: Modern Async Primitives on iOS, Android, and the Web
published: true
description: A look at some modern approaches to structured concurrency and programming asynchronously with collections in Swift, Kotlin, and TypeScript/JavaScript.
tags: async, swift, kotlin, javascript
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-11-17 18:59 +0000
---

# Let's Talk About Asynchronicity

When you began your programming journey, you started with synchronous, serialized code. One operation follows another, and so on, along a single timeline.

Soon after, you likely discovered the ability to execute code along multiple timelines. With this capability, your code might _still_ be synchronous, even if it splits its tasks across two timelines. But, your concurrent code could also be _asynchronous_, meaning that your main timeline continues along while some results are deferred until later.

![A comparison of synchronous and asynchronous program execution across two timelines](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3j03438z8h2ibim3xbe.png)
(Thanks [@AlisdairBroshar](https://twitter.com/AlisdairBroshar) for the [image](https://www.koyeb.com/blog/introduction-to-synchronous-and-asynchronous-processing).)

Arguably, a more interesting version of asynchronicity exists when multiple tasks are scheduled along a _single_ timeline. This is intuitively easy to understand: you go about your day on a single timeline, and you probably juggle many unrelated tasks as you do so. You have to _suspend_ one of your tasks to focus on the next one, and then the next one, etc. But unfortunately, those suspended tasks don't make any progress while you're not working on them.

![Along a single timeline, a task must be suspended in order to work on something new](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7v9y8ejzm057cy7lopv.png)
(Image [credit](https://doordash.engineering/2021/11/09/the-beginners-guide-to-kotlin-coroutine-internals/) to Sonic Wang @ DoorDash Engineering)

In this space, we also have the somewhat related term _blocking_. Java's NIO library is one well-known _non-blocking_ tool used for managing multiple tasks on a single Java thread. When listening to sockets, most of the time a thread is just blocked, doing nothing until it receives some data. So, it's efficient to use a single thread for monitoring many sockets, to increase the likelihood of the thread having some actual work to do. The [`Selector`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/Selector.html) API does this but is notoriously challenging to program well. Instead, developers use frameworks like [Netty](https://netty.io/) which abstract some of NIO's complexity and layer on some best practices.


![Java NIO's Selector API allows efficient reading from multiple channels using a single thread](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hfbpq5jbp7sxe5mvxu5.png)
(Thanks [GeeksForGeeks.org](https://www.geeksforgeeks.org/introduction-to-java-nio-with-examples/#) for the image.)


In the current generation of programming languages, there has been a renewed effort to simplify asynchronicity through _structured concurrency_. Language facilities like Swift's `async`/`await` or Kotlin's Coroutines allow for a unit of work to be suspended and resumed within the context of one or more timelines. Below, we're going to explore how some of these tools work.

# Swift: async/await & AsyncSequence

Swift's structured concurrency support was announced at Apple's WWDC '21 conference. [Kavon](https://twitter.com/call1cc) @ Apple gives a great intro to the topic [here](https://developer.apple.com/videos/play/wwdc2021/10134/). Swift's `async`/`await` support allows you to yield a flow of execution in your program while awaiting the result of a task. One example given in Kavon's presentation is to download some data and then await the result:
```swift
// Note `async let` here! Two tasks run concurrently.
async let (data, _) = URLSession.shared.data(for: imageReq)
async let (metadata, _) = URLSession.shared.data(for: metadataReq)
// Do other tasks here, while tasks execute
guard
  let size = parseSize(from: try await metadata),
  let image = try await UIImage(data: data)?.byPreparingThumbnail(ofSize: size)
else {
  throw ThumbnailFailedError()
}
return image
```

The beauty of this code is that all of the async work will be terminated if this block goes out of scope. This simplifies your mental model considerably and avoids entire classes of bugs.

> Remark: the `data` and `metadata` objects above might look a lot like JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)s, Rx [`Single`](https://reactivex.io/documentation/single.html)s, or Java [`Future`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html)s, if you've seen any of those before. (We'll talk about Promises, later.)

Swift >=5.5 also supports a new means of _unstructured_ concurrency, where you are responsible for governing the task lifecycle yourself, instead of letting Swift handle it implicitly.  For example, you can launch a Task to await the execution of some async function:
```swift
Task {
  await someAsyncFunction()
}
```

When using the `Task` construct, you need to be careful to `cancel()` the Task at the appropriate time when it goes out of scope.

Beyond these single-result-oriented constructs, Swift >=5.5 also supports async operations over _collections_. [`AsyncSequence`](https://developer.apple.com/documentation/swift/asyncsequence), also [announced at WWDC21](https://developer.apple.com/videos/play/wwdc2021/10058/) builds on earlier streamed value solutions like [RxSwift's `Observable`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/GettingStarted.md#observables-aka-sequences) or Combine's [`PassthroughSubject`](https://developer.apple.com/documentation/combine/passthroughsubject). But unlike those constructs, `AsyncSequence` can yield execution between iterations.

Let's see in action. First, let's take some ordinary for loops and wrap them in `Task`s. As noted above, the `Task` object will provide an execution context on the main [actor](https://developer.apple.com/videos/play/wwdc2021/10133/), in which async work could occur. 

```swift
Task { @MainActor in
    for f in [1, 2, 3] { print(f) }
}
Task { @MainActor in
    for s in [10, 20, 30] { print(s) }
}
```

As you would expect, since there isn't any use of `async` anywhere, these just run sequentially. The output is:
```console
1
2
3
10
20
30
```

Now, instead, let's try changing the array to an `AsyncSequence`, and running it again. Importantly, everything will still be on the main actor.

> Note for the code below: `.publisher` transforms the array into a Combine publisher, and `.values` creates an `AsyncSquence` from that Publisher.

```swift
Task { @MainActor in
    for await f in [1, 2, 3].publisher.values { print(f) }
}
Task { @MainActor in
    for await s in [10, 20, 30].publisher.values { print(s) }
}
```

This time, the loops yield execution after each value is emitted. The two tasks end up interleaving their output:

```console
1
10
2
20
3
30
```

The two tasks collaborate on a single thread of execution, almost as if they were operating concurrently. Pretty neat, right? 

One convenient place an `AsyncSequence` shows up is in this short-hand to read a network request's data, line-by-line. This can be achieved by async-iterating over the `lines` property on `URL`:

```swift
let rickMortyApi = "https://rickandmortyapi.com/api"
if let data = URL(string: rickMortyApi) {
    for try await line in data.lines {
        print(line)
    }
}
```

Another place you might encounter `AsyncSequence` is in SwiftUI's property wrappers. For example, let's suppose you have a field `@Published var credentials: [Credential]` in some `@ObservableObject`:

```swift
class User: ObservableObject {
  @Published var id: String
  @Published var credentials: [Credential]
}
```

You could iterate over this value like so:

```swift
for await credential in $user.credentials {
  // use credential
}
```

> Side note, if you [Migrate from the Observable Object protocol to the Observable macro](https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro), a lot of your `$` binding calls will probably go away, rendering this kind of iteration unnecessary.

This new functionality is not without some open questions, though. In his excellent blog from April 2022, [Using new Swift Async Algorithms package to close the gap on Combine](https://johnoreilly.dev/posts/swift-async-algorithms-combine/), John O'Reilley notes:

> As developers have started adopting the new Swift Concurrency functionality introduced in Swift 5.5, a key area of interest has been around how this works with the Combine framework and how much of existing Combine-based functionality can be replaced with async/await, AsyncSequence etc. based code.

In that blog, John makes a note of the [AsyncExtensions](https://github.com/sideeffect-io/AsyncExtensions) library, which has several cool [Features](https://github.com/sideeffect-io/AsyncExtensions#features) like `merge()`, `zip()`, etc., that you'd expect from other stream/collections API surfaces. 

You might also prefer to look at Apple's own evolution library for AsyncSequence, which offers a mostly-similar feature-set, called [swift-async-algorithms](https://github.com/apple/swift-async-algorithms#swift-async-algorithms). This library is more likely to be a conservative staging ground for forthcoming Swift language APIs.

# Kotlin: Coroutines, Flow

Many ecosystems have adopted the [`async`/`await`](https://en.wikipedia.org/wiki/Async/await) style of structured concurrency, but Kotlin took a slightly different approach. [Coroutines](https://en.wikipedia.org/wiki/Coroutine) is an older technology: the first usage of the term dates back to the late fifties and early sixties. The Kotlin implementation provides several improvements over prior implementations, however, such as native language integration, readability, structured concurrency, and error handling.

While many languages use `async` to denote an asynchronous function, Kotlin instead marks functions with `suspend`. Similar to what we saw with `AsyncSequence`, `suspend` introduces a _suspension point_ where execution may be yielded.

```kotlin
suspend fun getNetworkData(): Result<Response, Error> {
  // ... well, how bout it?
}
```

Just like Swift has support for structured concurrency, Kotlin can nest coroutines, too, s.t. canceling a top-level coroutine will also cancel its child coroutines. Let's consider an example. Below we use the `launch` coroutine builder to initiate a parent coroutine, and another `launch` coroutine builder to create a child coroutine within it.

```kotlin
coroutineScope.launch { // When I am canceled,
  launch { // I will cancel, too.
    getNetworkData()
  }
}
```

In Kotlin, the closest thing to Swift's `Task` is `GlobalScope.launch`, which can be used to initiate a coroutine that is not bound to its parent's scope.  Use of `GlobalScope` is generally not recommended, since it will require you to carefully manage the lifecycle of the returned `Job` object, calling `cancel()` when appropriate.

Consider the bit of code below as an example, wherein a coroutine is launched within another coroutine:

```kotlin
var inside: Job? = null
GlobalScope.launch {
  inside = GlobalScope.launch {
      delay(500)
      print("An inside job!")
  }
}.apply {
    delay(100)
    cancel()
}
inside!!.join()
```

This code will print:
```console
An inside job!
```

If you remove the `GlobalScope` designation, then the code prints nothing, since `inside` is automatically canceled.


Kotlin also has a construct for asynchronous collections/streams. Kotlin's version of `AsyncSequence` is called a `Flow`. Just as Swift's `AsyncSequence` builds upon prior experience with RxSwift and Combine, Kotlin's `Flow` APIs build upon earlier stream/collection APIs in the JVM ecosystem: Java's [RxJava](https://github.com/ReactiveX/RxJava#rxjava-reactive-extensions-for-the-jvm), [Java8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html), [Project Reactor](https://projectreactor.io/), and Scala's [Akka](https://akka.io/). 

Let's see `Flow` in action:
```kotlin
runBlocking {
    launch {
        flowOf(1, 2, 3).collect {
            yield()
            println(it)
        }
    }
    launch {
         flowOf(10, 20, 30).collect {
            yield()
            println(it)
        }
    }
}
```

The code above is single-threaded. If we weren't using `Flow`, we might expected the values to be emitted serially: 1,2,3,10,20,30. But, since each coroutine yields execution upon receiving a value, the outputs interleave:

```console
1
10
2
20
3
30
```

> The code above uses an explicit `yield()` operator to show where the code yields execution. As a technical note,  `collect { }` itself does not suspend, without `yield()`, even if the `Flow` would otherwise.

In practice, you find `Flow`s used heavily between the View and View Model layers of most modern apps using MVVM/MVI design patterns. Views commonly collect state updates from View Models using `Flow`s.

# JavaScript: aysnc/await, Promises, AsyncIterator/AsyncGenerator
`async`/`await` support has existed in Node.js and web browsers since 2017. Famously, `await` can be used to wait for a `Promise` to resolve or reject:

```javascript
async function fetchData() {
    const fetchPromise = fetch("https://rickandmortyapi.com/api")
    console.log("Awaiting your data...")
    const data = await (await fetchPromise).json()
    console.log("The data has arrived: ", data);
}

(async () => await fetchData())()
```

The code above works similarly to the unstructured concurrency primitives we saw with Swift's `Task` and Kotlin's `GlobalScope.launch`. The `fetch` function initiates some asynchronous work, bundles it up into a `Promise` object, and yields execution so the lines below it can proceed. Finally, we await the result of the Promise, and extract JSON data received from the network.

Unlike Swift and Kotlin, JavaScript does _not_ have any native support for structured concurrency. In fact, it doesn't even have native support for canceling unstructured tasks. There could be various reasons for this: JavaScript was one of the first mainstream languages to adopt `async`/`await` and its interpreters have traditionally been single-threaded. Nevertheless, this oversight has compelled the ecosystem to innovate its own unique solutions. As a well-known example, Axios provides a [`CancelToken`](https://axios-http.com/docs/cancellation) to facilitate task cancellation within that library.

However, JavaScript _does_ have async collection primitives analogous to those provided by Swift and Kotlin: [`AsyncIterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) and [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator).

Much like we saw with Swift's `AsyncSequence`, adding the `await` keyword into the generic `for` loop will create a suspension point wherein the JavaScript interpreter can yield its execution to other tasks. (See [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) documentation from Mozilla.)

```typescript
async function* asyncGenerator(multiplier: number = 1) {
  yield 1 * multiplier;
  yield 2 * multiplier;
  yield 3 * multiplier;
}

(async () => {
  for await (let i of asyncGenerator(0)) console.log(i);
})();

(async () => {
  for await (let i of asyncGenerator(10)) console.log(i);
})();
```

This code is single-threaded, but we again see two tasks using cooperative multitasking to yield execution to one another. The result is familiar:

```console
1
10
2
20
3
30
```

# Wrapping Up

Well, there you have it: a broad overview of structured concurrency and asynchronous collections programming in Swift, Kotlin, and JavaScript.

To help illustrate, here's a table comparing some of the machinery we discussed, across each of the three languages.

|                       | Swift                    | Kotlin                          | JavaScript               |
|-----------------------|------------------------|----------------------------------|-------------------|
| Launch async work (structured) | `async let`           | `launch { }`               | N/A               |
| Launch async work (unstructured) | `Task { }`           | `GlobalScope.launch { }`                | `Promise()`       |
| Cancel async work (unstructured) | `Task.cancel()`       | `Job.cancel()`                   | N/A               |
| Await completion of work | `await`                | (Implicit)                       | `await`           |
| Mark function as asynchronous | `async`               | `suspend`                        | `async`           |
| Async collection      | `AsyncSequence`        | `Flow`                           | `AsyncIterator` / `AsyncGenerator` |
| Async iteration syntax | `for await x in seq`   | `flow.collect { }`               | `for await (let x of gen)` |