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

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

Акции

Суть приложения с фильмами

Вызов API фильмов с помощью обратных вызовов

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

Перечисление Result позволяет нам классифицировать ответ как успешный или неудачный. В приведенном выше коде есть несколько моментов, на которые стоит обратить внимание. Во-первых, нам нужно убедиться, что мы получаем доступ к завершению в основном потоке, поскольку нам нужно обновить пользовательский интерфейс. Мы также должны убедиться, что вернулись после каждого из заявлений охраны. И, наконец, если бы нам пришлось сделать другой вызов, который зависит от первого вызова, тогда это станет более сложным, поскольку нам придется вложить второй вызов в обработчик успеха первого вызова.

Теперь давайте попробуем реализовать тот же сетевой вызов, используя подход async / await.

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

В iOS 15 и Xcode 13 Apple представила несколько API, поддерживающих асинхронность. Это включает в себя URLSession, HealthKit и т. Д. Как видите, мы используем новую функцию URLSession.shared.data, которая поддерживает асинхронность, и, следовательно, мы можем дождаться результата.

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

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

Вызов асинхронной функции

Если вы следуете шаблону проектирования MVVM, ваша модель представления будет отвечать за вызов функций Webservice (). GetMovies и возврат результата в представление для отображения. В приведенной ниже реализации наша модель представления MovieListViewModel предоставляет функцию fetchAllMovies, которая вызывает getMovies класса Webservice.

Поскольку функция getMovies в веб-сервисе является асинхронной и может генерировать вызовы, вызывающий должен использовать try await при ее вызове. Теперь представление может использовать MovieListViewModel и получать все фильмы.

Модификатор task имеет асинхронную поддержку, поэтому мы можем ожидать внутри модификатора. Если вы находитесь в ситуации, когда вы хотите ожидать выполнения асинхронной функции, но вы не находитесь в области асинхронности, вы можете использовать асинхронные закрытия, как показано ниже:

Замыкание async возвращает обработчик, который можно использовать для отмены задачи.

Реализация пользовательских асинхронных функций

В приведенном выше коде мы использовали поведение URLSession async / await, но что, если бы мы хотели реализовать async / await в наших собственных функциях.

Давайте посмотрим, есть ли у нас функция getStocks, как показано ниже:

Текущая реализация getStocks использует обратные вызовы для отправки массива акций обратно вызывающей стороне. Мы можем обновить эту функцию, чтобы получить к ней доступ через async / await. Это показано в реализации ниже:

Мы реализовали еще одну функцию getStocks, помеченную как async. Затем мы использовали withUnsafeContinuation, что-то вроде обещания. Замыкание withUnsafeContinuation вызывает getStocks, и когда getStocks возвращает данные, он возобновляет операцию. Вызывающий код в StockListViewModel показан ниже:

GetAllStocks в StockListViewModel не украшен асинхронностью, поскольку мы используем подход асинхронного закрытия. Еще одна вещь, на которую следует обратить внимание, - это атрибут @MainActor в классе StockListViewModel. @MainActor позволяет устанавливать наши свойства, такие как акции, в основном потоке, а не в фоновом потоке.

Заключение

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

Узнайте о новых функциях Async / Await & Actor на языке Swift. Посмотрите мой курс ниже: