Содержание Что важно: Асинхронное программирование в Dart характеризуется классами Future и Stream . Future представляет собой отложенное вычисление. Если нормальная функция возвращает результат, асинхронная функция возвращает объект Future (future), который в конечном итоге будет содержать результат. future вернет результат по завершению операции. Поток — это последовательность асинхронных событий. Это похоже на асинхронный итерируемый объект (Iterable), где вместо получения следующего события, когда вы его запрашиваете, поток сообщает о событии, когда оно будет готово. Потоки можно создавать разными способами, что является темой другой статьи, но все они могут использоваться одним и тем же способом: асинхронный цикл for (обычно вызываемый await for) выполняет итерацию событий потока, как цикл for итерацию по коллекции. Например: Этот код просто получает целочисленное событие из потока, складывает их и возвращает (future) сумму. Когда тело цикла заканчивается, функция приостанавливается до следующего события или завершения потока. Функция помечается ключевым словом async, которое требуется при использовании цикла await for. В следующем примере (на DartPad) выполняется проверка предыдущего кода путем создания простого потока целых чисел с помощью функции с async* (прим. генератор): Потоки выполнены, когда в них больше нет событий, и код, получающий события, уведомляется об этом так же, как он уведомляется о поступлении нового события. При чтении событий с помощью await for цикл заканчивается после завершения потока. В некоторых случаях ошибка возникает до завершения потока; возможно, сбой сети при извлечении файла с удаленного сервера или код, создающий события, содержит ошибку, кто-то должен знать об этом. Потоки могут сообщать о событии с ошибкой так же, как о событиях данных. Большинство потоков останавливаются после первой ошибки, но возможны потоки, отдающие более одной ошибки, и потоки, сообщающие о данных после события ошибки. В данном документе мы обсуждаем только потоки, которые отдают не более одной ошибки. При чтении потока с помощью await for ошибка выдается оператором цикла. Это также завершает цикл. Вы можете поймать ошибку с помощью try-catch. В следующем примере (на DartPad) возникает ошибка, если итератор цикла равен 4: Класс Stream содержит ряд вспомогательных методов, которые могут выполнять общие операции над потоком, похожие на методы Iterable. Например, можно найти наименьшее положительное целое число в потоке, используя lastWhere() из Stream API. Наиболее распространенный тип потока содержит последовательность событий, которые являются частями большего целого. События должны быть доставлены в правильном порядке без пропуска любого из них. Это тип потока, который вы получаете при чтении файла или получении веб-запроса. Такой поток можно слушать только один раз. Прослушивание позже может означать пропуск начальных событий, и тогда остальная часть потока не имеет смысла. Когда вы начнете слушать, данные будут извлечены и предоставлены кусками. Другой тип потока предназначен для отдельных сообщений, которые могут обрабатываться по одному. Такой поток можно использовать, например, для событий мыши в браузере. Вы можете начать слушать такой поток в любое время, и вы получите события, произошедшие во время прослушивания. Поток могу слушать несколько слушателей. Вы можете снова начать слушать события потока после отмены предыдущей подписки. Следующие методы в Stream<Т> обрабатывают поток и возвращают результат: Все эти функции, кроме drain() и pipe(), соответствуют аналогичной функции в Iterable. Каждый из них может быть легко написан с помощью асинхронной функции с циклом await for (или просто с помощью одного из других методов). Например, некоторые реализации могут быть такими:
All of these functions, except drain() and pipe(), correspond to a similar function on Iterable. Each one can be written easily by using an async function with an await for loop (or just using one of the other methods). For example, some implementations could be: (Актуальная реализация немного сложнее, но в основном по историческим причинам.) Следующие методы в Stream возвращают новый поток на основе исходного потока. Каждый из них ждет, пока кто-то прослушает новый поток, прежде чем слушать исходный. Выше указанные методы соответствуют аналогичным методам в Iterable, которые преобразуют итерируемый объект в другой итерируемый объект. Все это можно легко записать с помощью асинхронной функции с циклом await for. Функции asyncExpand() и asyncMap() похожы на функции expand() и map(), но позволяют аргументу функции быть асинхронной функцией. distinct() функции не существует в Iterable, но ее можно реализовать. Последние три функции более спецэфичны. Они включают обработку ошибок, которую не может выполнить цикл await for, так как первая же ошибка завершит цикл и его подписку на поток. С этим ничего не поделать. Вы можете исползовать handleError() для удаления ошибок из потока перед его использованием в цикле await for. Функция transform() предназначена не только для обработки ошибок; она является более обобщенным "map" для потоков. Для нормального map требуется одно значение для каждого входящего события. Однако, особенно для потоков ввода-вывода, для создания выходного события может потребоваться несколько входящих событий. StreamTransformer может помочь с этим. Например, дешифраторы, как Utf8Decoder, являются трансформаторами. Трансформатор требует только одну функцию bind(), которая может быть легко реализована через асинхронную функцию. Следующий код считывает файл и выполняет два преобразования в потоке. Сначала он преобразует данные из UTF8, а затем пропускает их через LineSplitter. Все строки печатаются, кроме тех, которые начинаются с хэштега (#). Метод listen() — это “низкоуровневый” метод, все остальные функции функция определяются через listen(). Чтобы создать новый тип потока, можно просто отнаследовать класс Stream и реализовать метод listen(), все другие методы Stream вызывают listen() для работы. Метод listen() позволяет начать прослушивание потока. Пока вы этого не сделаете, поток является инертным объектом, описывающим, какие события вы хотите слушать. При прослушивании возвращается объект StreamSubscription, представляющий активный поток, создающий события. Это похоже на то, как Iterable — это просто коллекция объектов, а итератор — это тот, кто делает фактическую итерацию. Подписку на поток можно остановить, возобновить ее после паузы и полностью отменить. Можно задать колбэки, которые будут вызываться для каждого события данных или события ошибки, а также при закрытии потока. Dart 2. Асинхронное программирование: futures Разработчик