Используйте классы Dart для работы с потоковыми данными

Концепция Streams оказалась сложной темой для понимания для большинства программистов, ныряющих в Dart (или любой другой язык в этом отношении), отчасти потому, что для понимания требуется пара попыток вместе с примерами. В этой статье я попытаюсь демистифицировать использование потоков в Dart, создавая что-то осязаемое с помощью того, что мы узнаем далее в этой серии.

Что такое потоки?

Глядя на документацию Dart, он определяется как:

Источник асинхронных событий данных. Stream предоставляет способ получить последовательность событий. Каждое событие является либо событием данных, также называемым элементом потока, либо событием ошибки, которое является уведомлением о том, что что-то не удалось. Когда поток передал все свои события, одно событие «готово» уведомит слушателя о том, что конец достигнут.

api.dartlang.org

Потоки как концепция относятся к каналу, по которому данные передаются из точки A в точку B. В этом канале мы можем выполнять различные преобразования данных, которые «считываются», прежде чем они достигнут точки. Б. Этот канал полезен при передаче данных фрагментами, а не целиком сразу.

Вы работаете с Streams в Dart с помощью набора вспомогательных классов, предлагаемых SDK. Эти вспомогательные классы предоставляют служебные методы для передачи данных в поток, а также уведомляют слушателей этого потока о необходимости захвата любых добавляемых данных.

Самый общий класс, представляющий поток, называется Stream<T>. Обычно мы не используем этот класс напрямую, потому что он скорее предоставляется другими классами в арсенале Dart. Рассматривайте это как интерфейс для взаимодействия с этим каналом, по которому проходят данные.

Базовый пример с StreamController ‹T›

StreamController<T> содержит поток, который позволяет потребителю отправлять ему данные, выполненные и ошибочные события. Мы могли бы получить доступ к этому потоку, выполнив streamController.stream, что позволит нам вызывать любой из методов, определенных в его документации.

Вот пример с классом StreamController<T>:

var streamController = StreamController();
// Accessing the stream and listening for data event
streamController.stream.listen((data) {
  print('Got eem! $data');
});

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

Итак, я предполагаю, что следующий вопрос звучит так: Как мы запускаем событие прослушивателя данных? Ответ: путем подачи данных в поток! Это стало возможным благодаря другому классу под названием EventSink<T>. Этот объект содержит add() метод подачи данных в поток:

streamController.sink.add('Added this string');
// Result
// Got eem! Added this string

Метод listen() в потоке также может перехватывать сообщения об ошибках. Это потому, что объект StreamSubscription<T> генерируется всякий раз, когда вы слушаете поток. Этот объект является причиной возможности обработки различных событий, таких как данные, ошибка и выполнение (когда в потоке вызывается метод close ()).

Вот полное определение метода listen():

StreamSubscription<T> listen (
  void onData(T event), 
  {
    Function onError,
    void onDone(), // Invoked when the stream is closed
    bool cancelOnError // Kills the stream when an error occurs
  });

Вот как бы мы назвали события «ошибка» и «готово»:

streamController.sink.addError('Houston, we have a problem!'); // Got an error! Houston, we have a problem!
streamController.sink.close(); // Mission complete!

Попробуйте это на DartPad

Потоки, предоставляемые через библиотеки

Хотя StreamController<T> позволяет нам детально управлять потоками, которые мы создаем сами, существуют встроенные библиотеки дротиков, которые используют потоки под капотом. Например, взгляните на этот фрагмент для настройки сервера:

import 'dart:io';
void main() async {
  var server = await HttpServer.bind('localhost', 8080);
  // HttpServer exposes a Stream<T> interface
  server.listen((HttpRequest request) {
    request.response.write('Hello, World!');
    request.response.close();
  });
}

Приведенный выше фрагмент создает экземпляр HttpServer для создания веб-серверов. Этот класс предоставляет интерфейс Stream<T>, что означает, что теперь мы можем прослушивать этот поток, который будет содержать объекты запроса, созданные, когда пользователь обращается к нашему серверу.

Вот еще один пример потока, отображаемого в веб-браузере:

import 'dart:html';
void main() {
  var button = querySelector('button');
  
  // `onClick` is a Stream<T> instance that receives user click data events
  button.onClick.listen((_) => print('Button clicked!'));
}

Взаимодействия пользователя, происходящие в браузере, такие как щелчок, прокрутка, ввод и т. Д., Генерируются как события «данных», захваченные в потоке. Другими словами, элементы HTML также предоставляют Stream<T> интерфейс для взаимодействия с пользователем на странице.

Существует гораздо больше классов, которые используют потоки под капотом, причем здесь дело в том, что, как правило, вы не будете создавать экземпляры Stream<T> объектов напрямую, а скорее они будут созданы для вас с помощью различных библиотечных классов в SDK.

Заключение

Потоки предоставляют мощный способ работы с порциями данных. Поскольку это работает асинхронно, мы получаем преимущество выполнения кода в неблокирующем режиме. Я бы рекомендовал прочитать документацию, особенно библиотеку dart: async, которая содержит классы для асинхронного программирования, такие как Streams и Futures.

В следующей части серии мы рассмотрим, как выполнять преобразования в потоке, а также продемонстрируем общий шаблон проектирования, основанный на использовании потоков 🧱

продолжить чтение

Как использовать потоки в Dart (Часть 2)

дальнейшее чтение

Делиться заботой 🤗

Если вам понравилось читать этот пост, поделитесь им в различных социальных сетях. Также проверьте и подпишитесь на мой канал YouTube (нажмите на значок колокольчика) для видео на Dart.

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

Ставьте лайки, делитесь и подписывайтесь на меня 😍, чтобы узнать больше о Dart.