Задачи создавать научились, поняли основную разницу между Task и Task.detached. Но что будет лежать внутри этих задач? Как этим управлять и как с этим работать?

Оглавление

Async Функции

Одно из назначений задач - это организация работы асинхронных функций. Функция помеченная ключевым словом async не вернёт результат сразу же. Она его вернёт через некоторое время. Именно поэтому данную функцию нельзя просто вызвать из синхронного кода. Такие функции могут вызываться либо в таких же асинхронных функциях, либо в Task.

func fetchUserFromDatabase() async throws -> User {
    .....
}

async - означает, что функция должна быть вызвана из асинхронного контекста.
throws - означает, что функция может выкинуть исключение

Пример

Task {
	do {
		let user = try await fetchUserFromDatabase()
		let userAvatar = try await getAvatar(userId: user.userId)
		/// Последовательное исполнение задач, сначала загрузится пользователь,
		/// затем начнётся подгрузка аватара пользователя
	} catch {
		/// Обработка ошибок
	}
}

Await

В примере выше мы уже использовали ключевое слово await. Вы скорее всего догадываетесь, что await нужен для того чтобы дождаться результата выполнения асинхронной функции. Но что именно происходит в момент вызова await?

await - это место, в котором останавливается выполнение кода функции - Suspention point. Остановка происходит до тех пор пока написанная после await функция не вернёт какое-нибудь значение. Что происходит с потоком на котором выполнялась функция? Если код приостанавливается и ждёт выполнения функции, то где он ждёт?

ОЧЕНЬ ВАЖНО

На самом деле, когда мы пишем Await происходит следующее:

  1. Создаётся состояние (continuation), в которое захватываются необходимые переменные, ссылки
  2. Созданное состояние передаётся исполнителю на выполнение
  3. Вместе с передачей состояния осуществляется и передача освободившегося потока. Далее уже исполнитель сам будет решать какую задачу запускать на этом потоке. Исполнитель также учитывает приоритет поступающих к нему задач.

Из выше описанных пунктов следует, что после того как мы вернёмся в остановленную функцию с результатом выполнения, то поток будет отличаться от потока с которого начиналось выполнение.

Task {
	/// Здесь мы были например на Thread 5
	let user = try await fetchUserFromDatabase()
	/// Здесь мы уже будет на другом потоке (иногда можем попасть и на тот же самый)
}

Task.yield