Каковы основные принципы работы с async/await в Swift?

Ответ

Async/await — это модель асинхронного программирования в Swift, позволяющая писать линейный, легко читаемый код вместо использования completion handlers или цепочек Future/Promise.

Основные принципы:

  1. async — маркирует функцию как асинхронную. Такая функция может быть приостановлена (suspended).
  2. await — указывает точку приостановки, в которой выполнение может быть передано другим задачам, пока ожидается результат асинхронного вызова.
  3. Structured Concurrency: Асинхронные задачи выполняются в структурированной иерархии, что упрощает управление жизненным циклом и отмену.

Базовый пример:

// 1. Объявление асинхронной функции
func fetchUserData(from url: URL) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url) // 'await' — точка приостановки
    return try JSONDecoder().decode(User.self, from: data)
}

// 2. Вызов в асинхронном контексте (Task)
Task {
    do {
        let user = try await fetchUserData(from: someURL)
        await MainActor.run { // Возврат в главный поток для UI-обновлений
            self.usernameLabel.text = user.name
        }
    } catch {
        print("Fetch failed: (error)")
    }
}

Ключевые преимущества:

  • Читаемость: Код выглядит как синхронный, нет "адской пирамиды" из замыканий.
  • Безопасность: Компилятор контролирует правильное использование асинхронного контекста.
  • Интеграция с ошибками: Полная поддержка try/catch.
  • Отмена задач: Встроенная поддержка через Task.checkCancellation() и Task.isCancelled.

Важное ограничение: Функции с await можно вызывать только из асинхронного контекста (другой async-функции или блока Task).

Ответ 18+ 🔞

Ну вот, смотри, опять эти ваши асинхронные штуки. Раньше, блядь, жили спокойно — completion handlers, цепочки промисов, адская пирамида из замыканий, в рот меня чих-пых! Код читаешь — глаза сломать можно, будто мартышлюшка на клавиатуре плясала.

А теперь, ёпта, придумали async/await. И знаешь что? Овердохуища удобно, ей-богу!

Суть, если по-простому:

  1. async — это как бирка на функцию: «Эй, чувак, я могу зависнуть, не торопи меня, блядь!».
  2. await — это точка, где ты говоришь: «Ладно, я подожду тут, а ты, система, иди пока другие дела поделай, не стой столбом».
  3. Structured Concurrency — это чтоб задачи, как дети малые, по иерархии бегали, а не как угорелые по всему процессу. Упрощает жизнь — отменить их или отследить проще.

Смотри, как теперь красивше:

// 1. Вот тебе асинхронная функция. Видишь `async`? Всё, она особенная.
func fetchUserData(from url: URL) async throws -> User {
    // Ждём данные с сервака. `await` — тут мы можем приостановиться.
    let (data, _) = try await URLSession.shared.data(from: url)
    // Декодируем и валим обратно.
    return try JSONDecoder().decode(User.self, from: data)
}

// 2. А вызываем вот так, в Task'е.
Task {
    do {
        // Красота же? Прям как синхронный код! Никаких лесенок из замыканий!
        let user = try await fetchUserData(from: someURL)
        // Обновляем интерфейс, возвращаемся на главный поток.
        await MainActor.run {
            self.usernameLabel.text = user.name
        }
    } catch {
        // А ошибки ловим как у нормальных людей, try/catch.
        print("Fetch failed: (error)") // Ну, тут понятно, всё просралось.
    }
}

И чем это, блядь, хорошо?

  • Читаемость — нихуя не надо глаза ломать, код линейный.
  • Безопасность — компилятор, хитрая жопа, сам следит, чтоб ты по правилам играл.
  • Ошибки — наконец-то можно try/catch использовать, а не по колбэкам их раскидывать.
  • Отмена — встроенная, можно проверить, не отменили ли тебя: Task.checkCancellation().

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