В одной из наших предыдущих бесед [«Могут ли Kotlin-coroutines полностью заменить потоки?»] была затронута тема вопроса: Какие различия существуют между Dart async/await, Kotlin suspend и асинхронностью в JavaScript?
На самом деле, как Dart async/await, так и Kotlin suspend являются просто синтаксическим сахаром, то есть способом представления кода таким образом, чтобы он был более понятным и структурированным. В этом плане они не имеют различий — это всего лишь синтаксический сахар.
Однако если вы хотите поговорить о реализациях этих концепций, то здесь уже можно найти некоторые отличия.
В данной статье мы кратко обсудим синтаксический сахар и закончим рассказом об "Easter egg" Flutter в ColorOS Oppo.
Вы наверняка знаете, что в Dart async/await на самом деле работает через известный вам объект Future
. Например, вот пример использования async/await:
Future<int> doSomething() async {
whatTF();
var x = await someIntFuture();
return godGG(x);
}
На практике этот код эквивалентен следующему:
Future<int> foo() {
whatTF();
return someIntFuture().then((x) {
return godGG(x);
});
}
Здесь await
фактически преобразуется в вызов метода Future.then
.В реализации Future
, которая находится в файле future_impl.dart, используется приватный метод _thenAwait
, который служит для реализации синтаксического сахара await
. Этот метод регистрирует новый Future
и создаёт _FutureListener
.
Основное отличие заключается в том, что then
проверяет корректность переданной функции ошибок (onError
) заранее, тогда как её вызов осуществляется в модуле Dart sdk/lib/_internal/vm/lib/async_patch.dart:
Основная функция создания асинхронных колбэков заключена в методе
_createAsyncCallbacks
. Второй метод _await(Object? object)
принимает объект типа Object?
, так как все видимые типы в Dart являются подтипами Object
, а все функциональные типы — подтипами Function
. При этом сам Function
также является типом Object
:
int doSomeThing() {
return 1;
}
void main() {
// выводит true
print('${doSomeThing is Object}');
}
Для более подробного ознакомления с типами см.: https://juejin.cn/post/6968369768596242469
А вот как показано ниже, на самом деле методы then
и thenAwait
имеют мало различий. То есть реализация then
и await
практически одинакова. Различия между stateThenAwait
, stateThen
и stateThenOnError
заключаются в небольших отличиях в стеках трассировки:
Ого, а вы заметили, что у then
и _thenAwait
есть состояние? Почему же в объекте Future
есть состояние? Это можно объяснить с помощью стеков трассировки:Система выполнения использует awaiter stack trace, чтобы улучшить отслеживание стека. Каждый awaiter представляет собой замыкание или поддерживаемую приостановкой async
функцию, и каждый awaiter состоит из пары (замыкание, следующий кадр)
:
следующий
— это объект, представляющий следующего awaitera, как если бы это был связанный списокИз диаграммы путей через Awaiter Stack Traces видно, что в процессе существует ключевой объект _SuspendState
.
Увидев _SuspendState
, вы можете вспомнить suspend в Kotlin? Верно, этот объект отвечает за управление состоянием приостановленной асинхронной операции внутри Dart:
Более абстрактно:
_SuspendState._initAsync
создаёт экземпляр _Future<T>
, который используется как результат async
функции, и этот экземпляр хранится в переменной :suspend_state
C++ виртуальной машины
Затем из _SuspendState._await
возвращаются _SuspendState._returnAsync
, _SuspendState._returnAsyncNotFuture
и _SuspendState._handleException
, которые используются как результат async
функции.- При первом вызове _SuspendState._await
создаются замыкания для then
и error
, которые восстанавливают выполнение асинхронной функции через _resume
. Если параметр await
является Future
, то _SuspendState._await
добавляет замыкания then
и error
к этому Future
, в противном случае он просто планирует микротаск для продолжения выполнения приостановленной функции. Речь идет о том, что в файле sdk/lib/_internal/vm/lib/async_patch.dart реализован метод _SuspendState._await
, который отвечает за выполнение выражения await
.
Кроме того, обработка исключений также осуществляется через метод _handleException
внутри _SuspendState
. Можно заметить, что вся реализация Future для async/await основана на состоянии машины _SuspendState
, которая обеспечивает возможность приостановки и восстановления.
Если объяснять реализацию синтаксического сахара для async/await, то это просто Future
/ then
. На самом деле, Dart преобразует его в машину состояний для управления выполнением функций и приостановками в await
. Это позволяет использовать синхронный стиль для написания асинхронного кода, повышая его читаемость.
В Kotlin suspend-функции также могут быть приостановлены и возобновлены без блокировки выполняющего их потока. Они представляют собой синтаксический сахар, позволяющий разработчику писать асинхронный код в синхронном стиле.
Добавление ключевого слова suspend
фактически сообщает компилятору, что данная функция может быть приостановлена.
При обработке suspend-функций компилятором они преобразуются в соответствующую внутреннюю реализацию, известную как CPS (Continuation Passing Style). Основной момент здесь — продолжение (continuation), которое хранит состояние функции, локальные переменные и контекст выполнения.Продолжение обычно сохраняет ссылку на продолжение вызывающей стороны, поэтому они образуют цепочку продолжений, которую можно использовать для генерации трассировки стека.
Например, простое описание suspend-функции заключается в том, что она переопределяется как процесс, принимающий параметр типа Continuation:
suspend fun getUser(): User?
suspend fun setUser(user: User)
suspend fun checkAvailability(flight: Flight): Boolean
fun getUser(continuation: Continuation<User?>): Any?
fun setUser(user: User, continuation: Continuation<Unit>): Any
fun checkAvailability(
flight: Flight,
continuation: Continuation<Boolean>): Any
На практике это выглядит примерно так:
// Преобразование до
suspend fun fetchData(): String {
println("Начало получения данных")
val result = networkRequest()
println("Данные получены: $result")
return result
}
suspend fun networkRequest(): String {
delay(1000)
return "Привет, мир!"
}
Можно заметить, что компилятор Kotlin генерирует сопрограммы, аналогичные концепции машины состояний, которая отслеживает состояние выполнения функции, например, используя метку для отслеживания места возобновления.
Поэтому, с точки зрения дизайна, сопрограммы в Kotlin представляют собой сочетание машин состояний и передачи управления, что позволяет реализовать модель, позволяющую приостанавливать и возобновлять выполнение.
Наконец, немного отступлю от темы. Вчера мне попалась статья Алекса в группе, где он делится скриншотами вывода ColorOS Живого острова во время работы. Можно заметить, что UI-рендеринг ColorOS Живого острова реализован с помощью Flutter:
Интересно отметить, что ранее на платформе разработчиков OPPO (разработка для OPPO) также были представлены логические данные экрана "Живого" острова. Тогда можно было видеть, что эти данные тоже основаны на Flutter:
Однако стоит отметить, что верхний уровень или приложения, использующие этот подход, могут не быть написаны на Dart, а использовать свои собственные шаблоны для поддержки этого метода. Это очень похоже на подход WeChat Mini Programs Skyline:
Поэтому это ещё одна малоизвестная сторона Flutter.## Дополнительные материалы
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )