Введение

Мы знаем что наш код выполняется сверху вниз, каждая следующая операция исполнится только после текущей - это называется синхронное программирование. Но довольно часто нам нужно выполнять “операции с ожиданием”, такие как, например, запрос в сеть, ведь запрос может быть удачным, а может быть не удачным (например статус код 404), и поэтому нам надо дождаться ответа от сервера, чтобы оповестить пользователя об успешности или неуспешности запроса. Проблема в том, что ожидание - это тоже операция, ведь нам надо будет непрерывно проверять не пришел ли нам ответ с сервера, а тк мы знаем, что одномоментно у нас может выполняться только одна задача, то, как вы понимаете, следующие задачи у нас не будут выполняться пока не придёт ответ с сервера, а ответ может приходить долго. Соответственно, из-за ожидания мы просто блокируем всю нашу систему.

Как же нам быть в этой ситуации ? Тут-то нам на помощь и приходит асинхронное программирование.

???? Асинхронное программирование — концепция программирования, при которой мы закидываем процесс ожидания ответа от какого-то сервиса в бекграунд, тем самым не блокируя поток выполнения задач.

Dart, как и, наверное, почти любой другой язык, предоставляет нам возможности не только для синхронного программирования, но и для асинхронного, благодаря таким инструментам как Future и Stream.

Future

Класс Future - наш помощник в создании асинхронных операций.

У Future есть 3 состояния:

  • Uncompleted - незавершенное, операция ещё не запущена или в процессе выполнения

  • Completed with result - операция завершена успешно

  • Completed with error - операция завершена с ошибкой

Пример Future:

import 'dart:async';

Future<String> fetchData() {
  // Создаем и возвращаем объект Future
  return Future(() async {
    // Имитируем получение данных с сервера через 2 секунды
    await Future.delayed(Duration(seconds: 2));
    // Возвращаем результат
    return "Hello World";
  });
}

void main() {
  fetchData().then((data) {
    print('Fetched data: $data');
  }).catchError((error) {
    print("Error fetching data: $error");
  });
}

В этом примере, мы используем конструктор Future для создания нового объекта Future, который будет запущен асинхронно. Внутри этой функции мы имитируем получение данных с сервера с помощью Future.delayed(Duration(seconds: 2)). Это задерживает выполнение функции на 2 секунды, имитируя время ожидания ответа от сервера. После этого мы используем ключевое слово return чтобы вернуть результат "Hello World" и завершить функцию.

Когда вы создаете объект Future, он находится в состоянии ожидания, и вы можете добавить функцию, которая будет выполнена, когда Future завершится. Эта функция называется "callback" или "обработчик", её следует передать в метод then. Но then можно и не использовать.

Если выполнение Future завершается с ошибкой, то можно использовать метод catchError для обработки исключения, он перехватывает исключения, которые были выброшены в Future. В этом примере catchError использован для обработки ошибки и вывода сообщения в консоль. Если бы мы не использовали catchError, то исключение просто было бы выброшено и программа бы завершилась с ошибкой.

Таким образом, метод catchError является удобным способом для обработки ошибок в Future и позволяет продолжать выполнение программы в случае ошибки.

async / await

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

void main() async {
  // Пробуем получить данные
  try {
    final data = await fetchData();
    print('Fetched data: $data');
  } catch (error) {
    // catch вызывается в случае получения ошибки с блока try
    print("Error fetching data: $error");
  }
}

Future<String> fetchData() async {
  // Имитируем ожидание от сервера
  await Future.delayed(Duration(seconds: 2));
  // Возвращаем якобы полученные данные
  return "Hello World";
}

Event Loop

Event Loop - это вечный цикл, выполняющий все задачи

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

Есть 2 очереди задач: Event и MicroTask

Очередь MicroTask

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

Очередь microtasks имеет приоритет над обычными и опустошается первой при каждой итерации эвент лупа. Очередь microtasks редко используется разработчиками.

Очередь Event

Используется для планирования операций, которые получают результат от:

  • внешних событий, таких как

    • операции ввода/вывода

    • жесты

    • рисование

    • таймеры

    • потоки

    • ...

  • Future

Фактически, каждый раз при срабатывании внешнего события, соответствующий код ставится в очередь Event.

Как только оказывается, что очередь MicroTask пуста, Event Loop берёт первую задачу из очереди Event и исполняет её.

Futures также обрабатываются с помощью очереди Event.

Как работает Future в Event Loop ?

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

  2. Когда функция выполнилась, то после этого отрабатываются методы then, если код выполнился успешно, или catchError, если код выполнился с ошибкой.

Заключение:

Future - чрезвычайна полезная и обязательная вещь для изучения Flutter программистам, понимание этой темы поможет избежать немалого кол-ва ошибок про работе с асинхронщинной и даст понимание в работе с сервером.

Спасибо за прочтение, жду отзывы и правки, если таковые имеются.

Источники:

  1. https://tproger.ru/articles/asynchronous-programming/

  2. https://vk.com/@bonch_dev-isolate-and-event-loops-future-asyncawait

  3. https://chat.openai.com

  4. https://youtu.be/f_KdDUWgef8

  5. https://metanit.com/dart/tutorial/7.1.php

  6. https://habr.com/ru/post/497278/

  7. https://yandex.ru/video/preview/5743019436470613120

Комментарии (3)


  1. PackRuble
    00.00.0000 00:00
    +1

    Спасибо, достаточно просто для понимания написано. ????
    Хотелось бы увидеть примеры в разделе про Event Loop, и в каких ситуациях можно применить microtask.


  1. avenumDev
    00.00.0000 00:00

    лаконично и понятно в целом.

    Жаль о стримах ни слова, жду 2-ю часть


    1. evklidtop Автор
      00.00.0000 00:00

      Спасибо за идею ????