Что можно и что нельзя делать с Async/Await

97a5e28acb037e793d0bafc377d32a93.jpg

Синтаксис async/await, введенный в Swift 5.5, значительно упростил асинхронное программирование, сделав его более доступным и интуитивно понятным. Однако, как и любой мощный инструмент, он может быть использован неправильно. Здесь я хочу рассмотреть пять распространенных ошибок, которые разработчики часто допускают при использовании async/await и предложить стратегии их избегания.

Ошибка 1: Необработка Ошибок

Асинхронные функции Swift могут вызывать ошибки, так же как и их синхронные аналоги. Однако многие разработчики, особенно те, кто только начинает работать с синтаксисом async/await, могут упускать обработку ошибок, что приводит к сбоям или непредсказуемому поведению.

Решение

Синтаксис do-catch в Swift — ключ к обработке ошибок из асинхронных функций. Обернув вызов асинхронной функции в блок do-catch, вы можете перехватить и обработать любые выброшенные ошибки, предотвратив сбои и обеспечивая предсказуемое поведение вашего приложения.

do {
    let result = try await someAsyncFunction()
} catch {
    print("An error occurred: \(error)")
}

Ошибка 2: Блокировка Главного Потока

Блокировка главного потока — это распространенная ошибка, которая может привести к замедлению работы приложения и ухудшению пользовательского опыта. Несмотря на введение async/await, по-прежнему можно случайно заблокировать главный поток, если не быть внимательным.

Решение

Длительные задачи следует переносить на фоновую очередь, чтобы избежать блокировки главного потока. В Swift 5.5 был введен API Task, который можно использовать для указания контекста выполнения асинхронных функций.

Task.init(priority: .background) {
    do {
        let result = try await someAsyncFunction()
    } catch {
        print("An error occurred: \(error)")
    }
}

Ошибка 3: Игнорирование Отмены

При использовании async/await разработчики часто упускают из виду отмену задач, оставляя задачи в работе даже после того, как они становятся не нужны. Это может привести к пустой трате ресурсов и потенциальному замедлению работы.

Решение

С помощью API Task в Swift вы можете отменять задачи, когда они больше не нужны. Также хорошей практикой является проверка на отмену внутри ваших асинхронных функций и соответствующий отклик на нее.

let task = Task {
    do {
        let result = try await someAsyncFunction()
    } catch {
        print("An error occurred: \(error)")
    }
}

task.cancel()

Ошибка 4: Неправильное Понимание Семантики Await

Распространенное недопонимание с await заключается в том, что разработчики ожидают, что оно будет продолжать операцию в фоновом режиме. Однако, await приостанавливает текущий контекст выполнения до завершения ожидаемой задачи.

Решение

Понимание семантики await является ключевым. Если вам нужно, чтобы несколько операций выполнялись одновременно, рассмотрите возможность использования API TaskGroup для структурированной конкурентности или создайте отдельные экземпляры Task.


Task {
    do {
        try await withThrowingTaskGroup(of: Void.self) { group in
            group.async {
                let result1 = try await someAsyncFunction1()
            }
            
            group.async {
                let result2 = try await someAsyncFunction2()
            }
        }
    } catch {
        print("An error occurred: \(error)")
    }
}

Ошибка 5: Чрезмерное использование Async/Await

Async/await — мощный инструмент, но, как и любой инструмент, он не всегда является лучшим решением для каждой проблемы. Некоторые разработчики попадают в ловушку, используя async/await повсеместно, что может привести к ненужной сложности и даже проблемам с производительностью.

Решение

Помните, async/await следует использовать, когда преимущества асинхронности ясны. Если операция быстрая и не включает блокирующие задачи (такие как сетевые запросы или операции с файлами), то синхронный код может быть проще и эффективнее.

// Синхронная версия
func fetchData() -> Data {
    // несколько быстрых неблокирующих операций
}

// Асинхронная версия
func fetchData() async -> Data {
    // используйте async только при наличии блокирующих операций
}

Заключение

Понимание и избегание этих ошибок — таких как пренебрежение обработкой ошибок, блокировка главного потока, игнорирование отмены задач, неправильное толкование семантики await и чрезмерное использование async/await — критически важно для эффективного использования этой функции. Твердое понимание синтаксиса и семантики async/await является ключом к полному раскрытию его потенциала.

По сути, хотя async/await и является значительным прогрессом в Swift, это не панацея. Это инструмент, и его эффективность зависит от того, как он используется. Будучи внимательными к этим распространенным ошибкам, разработчики могут использовать async/await наиболее полно, создавая надежный, эффективный и поддерживаемый асинхронный код, который приводит к лучшему, более плавному пользовательскому опыту.

© Habrahabr.ru