Swift - Concurrency
Published: 2022-04-05 08:57:07

- Asynchronous code - can be suspended and resumed later.
- Parallel code - multiple pieces of code run simultaneously.
- A program that uses parallel and asynchronous code carries out multiple operations at a time; it suspends operations that are waiting for an external system, and makes it easier to write this code in a memory-safe way.
- concurrency to refer to this common combination of asynchronous and parallel code.

### Asynchronous Functions
- **synchronous** functions and methods, which either run to completion, throw an error, or never return.
- Asynchronous function or method still does one of those three things, but that can be suspended while it’s partway through execution.

-  To indicate that a function or method is asynchronous, you write the **async** keyword in its declaration.

- When calling an asynchronous method, execution suspends until that method returns. Use **await** in front of the call to mark the possible suspension point

example :

``` swift
func listPhotos(inGallery name: String) async -> [String] {
    let result = // some asynchronous networking code
    return result
}
```

``` swift
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
```

Places in your program can call asynchronous functions or methods:

  1. Code in the body of an asynchronous function, method, or property.
  2. Code in the static main() method of a structure, class, or enumeration that’s marked with `@main`.
  3. Code in an unstructured child task

In above example, Instead of returning whole array at once after all of the array’s elements are ready, wait for one element of the collection at a time using an **asynchronous** **sequence** using **for-await-in**  loop.

example :

``` swift
import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}
```
for-await-in loop can be used for any types that conforms to the AsyncSequence protocol. Similarly for-in for Sequence.

---

### Calling Asynchronous Functions in Parallel
- Calling an asynchronous function with await runs only one piece of code at a time.

- To call an asynchronous function and let it run in parallel with code around it, write **async** in front of **let** when you define a constant, and then write **await** each time you use the constant.

example :

``` swift
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
```

- Call asynchronous functions with **await** when the code on the following lines depends on that function’s result. This creates work that is carried out **sequentially**.
- Call asynchronous functions with **async-let** when you don’t need the result until later in your code. This creates work that can be carried out in **parallel**.

---

### Structured Concurrency
A task is a unit of work that can be run asynchronously as part of your program. All asynchronous code runs as part of some task.
- Task can be added as a child to a Task Group.
- **async-let** syntax creates a child task.
- Each task in a task group has the same parent task, and each task can have child tasks. Because of the explicit relationship between tasks and task groups, this approach is called structured concurrency

### Unstructured Concurrency
- Unstructured task doesn’t have a parent task.
- To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:) initializer.
- To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:) class method. 

---

### Actors

- Actors are reference types. 
- Unlike classes, actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor.
- You create an instance of an actor using the same initializer syntax as structures and classes. When you access a property or method of an actor, you use await to mark the potential suspension point.

example :

``` swift
actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}
```

``` swift
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
```