image

В данной серии статей я не буду касаться азов программирования на самом Dart, а коснусь вопросов асинхронности в Dart.

Библиотеки Dart полны функций, которые возвращают Future или Stream объекты. Эти функции асинхронны.

В документации читаем:

Future — подобен обещанию когда-то в будущем предоставить результат.
Stream — это способ получить последовательность значений, таких как, например, последовательность событий (поток).

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

Импортируем библиотеку так:

import 'dart:async';


Future


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

Использовать async и await


Это, наверное, самый простой и понятный способ обработки future-объектов. Он очень похож на синхронный.

someFunction() async {
  var someVar = await functionXXL();
  if (someVar == someParam) {
    // Делаем что-то.
  } else {
    // Делаем что-то.
  }
}

В чем суть await: await ожидает возвращения результата асинхронной функции.

async функция (someFunction() — в нашем примере) фактически возвращает сразу, до того, как выполнятся асинхронные операции внутри нее, но возвращает она Future, т.е. возвращает «обещание» вернуть определенные значения позже, когда асинхронные процессы будут завершены.

Внутри функции с ключевым словом async мы можем запустить один или несколько асинхронных процессов. Другими словами конструкций await может быть много (пример из оригинальной документации):

runUsingAsyncAwait() async {
  //...
  var entrypoint = await findEntrypoint();
  var exitCode = await runExecutable(entrypoint, args);
  await flushThenExit(exitCode);
}

Важно обратить внимание на следующее:

1. await работает только внутри тела функции, которое предварено ключевым словом async.
2. await конструкции выполняются в том же логическом порядке, как они написаны. Т.е. пока не выполнился один await управление не передается следующему.
3. async функции всегда возвращают Future. Если мы не хотим, чтобы наша функция возвращала Future, можно использовать различные решения для этого, например, мы можем вызвать async функцию из другой (не async) функции.

Теперь давайте посмотрим, как происходит обработка ошибок в async функциях:

someFunc() async {
// какая-то логика
  try {
    await someObject.start();
  } catch (e) {
    // управление ошибками
  }
}

Используем метод Future — then()


Этот вариант имеет воплощение более похожее на асинхронное. Рассмотрим на примере (пример из оригинальной документации):

HttpRequest.getString(url).then((String result) {
  print(result);
}).catchError((e) {
  // Обрабатываем или игнорируем ошибку.
});

В этом примере HttpRequest.getString(url) возвращает Future. Когда Future завершается, метод then() выполняет некоторый код, в нашем случае выводит строку в консоль. Обратите внимание на, что обработка ошибок в этом случае происходит в конструкции catchError.

Шаблон then().catchError() является сам по себе асинхронной версией try-catch.

Метод then() возвращает Future, обеспечивая удобный способ запуска нескольких асинхронных функций в определенном порядке. Если функция обратного вызова (callback), зарегистрированная в then(), возвращает Future, then() возвращает эквивалентный Future. Если callback возвращает значение любого другого типа, then() создает новый Future, который завершается с этим значением.

Опять пример из оригинальной документации:

Future result = costlyQuery();

return result.then((value) => expensiveWork())
             .then((value) => lengthyComputation())
             .then((value) => print('done!'))
             .catchError((exception) => print('DOH!'));

Здесь мы видим «сцепление» нескольких .then().

Функции в этом примере отрабатывают в следующем порядке:

1. costlyQuery()
2. expensiveWork()
3. lengthyComputation()

Используем метод Future.wait()


Если нам нужно выполнить несколько асинхронных функций, а по окончании их выполнения выполнить некоторый код, можно использовать статический метод Future.wait(). Пример:

Future func1 = doSmth1();
Future func2 = doSmth2();
Future func3 = doSmth3();;

Future.wait([func1, func2, func3])
    .then((List values) {
      print('Отработали!');
    });

Ссылки по теме


Асинхронное программирование Dart (eng): (ссылка 1, ссылка 2).
Также можно почитать: (ссылка 3, ссылка 4)

Я не гуру в Dart, поэтому буду очень благодарен за ваши замечания, уточнения и предложения. Спасибо за внимание.

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


  1. rock
    05.05.2015 12:34

    То есть, в плане Future / async / await, Dart абсолютно ничем не отличатся от JS Promise (ES6) / async / await (ES7):

    Future -> Promise
    future.then -> promise.then
    future.catchError -> promise.catch
    Future.wait -> Promise.all
    fn() async { } -> async function fn(){ }
    await fn() -> await fn()
    


    1. degorov
      05.05.2015 13:45

      Так создатели Dart изначально говорили, что внедряются только те фичи и только так, чтобы это всё потом можно было бы затранспайлить во вменяемый JS-код. А теперь, когда они официально дропнули dart vm в браузере, уж и подавно.


    1. TheSteelRat
      05.05.2015 15:00

      Да, только в Dart они уже есть в стабильной ветке, собраны в одном месте, работают и компилируются в JS. В родном JS они повсеместно придут ещё не скоро.


      1. rock
        05.05.2015 15:08
        -2

        В JS async / await я использую больше года. Promise — несколько лет. Babel, можно отдельно regenerator, traceur — ну да, совсем не скоро :)


        1. TheSteelRat
          05.05.2015 15:26

          Тут смотря как сравнивать. Я вам говорю про «голые» языки без дополнительных тулзов. В тех средах где запускается Dart, stream/future/async/await работают из коробки. Вышеперечисленные фичи JS без компиляции мало где заработают.


          1. rock
            05.05.2015 15:36

            1. kelegorm
              05.05.2015 20:32

              Я понял так, что говорили о том, что Dart сам по себе имеет все фичи из коробки, и они работают. Когда ведется разработка с использованием дарта, все запускается в браузере Dartium, где дарт как раз не компилируется и работает нативно. А перед паблишем уже идет компиляция в js. Все из коробочки.


              1. rock
                06.05.2015 09:47

                Не понимаю. Я говорил только о том, что этот функционал в Dart и JS идентичен — а мне начинают доказывать, что это функционал есть Dart а в JS будет неизвестно когда, при том, что согласно новой стратегии развития Dart, в первую очередь, компилируемый в JS язык и с этой позиции ничем не отличается от грядущих стандартов JS, которые также где-то доступны из коробки, а где-то требуют полифилов и компиляции в старый синтаксис. Кроме, разве что, того, что повсеместное внедрение этих стандартов планируется, а Dart'a — нет.


                1. kelegorm
                  06.05.2015 11:05
                  +1

                  Ну так-то да, текущая ситуация такова, что оба языка должны компилироваться в старый нормальный JS с какими-то костылями, и для языка, и для браузера.

                  Но разница есть, в пользу дарта следующая:
                  1. Разрабатывая на дарте, можно пропускать этап компиляции в JS (в Dartium отлаживаем же) — это экономия времени.
                  2. У дарта есть некая инфраструктура, ничуть не хуже node.js и npm, и многие считают даже более вревосходящей.

                  То есть, на данный момент, разработка на дарте будет несколько удобнее, чем на JS 6.

                  Но может я чего-то не знаю, и у JS 6 есть чем здесь ответить, поправьте меня в таком случае.


            1. ko11ega
              06.05.2015 00:18
              +1

              Компилировать в JS нужно только один раз перед выкладыванием кода в продакшн.
              При разработке, внеся изменения в код, мы просто нажимаем F5 в Dartium и мгновенно можем увидеть работу новой версии кода.
              Это очень убыстряет разработку.


  1. oberon87
    05.05.2015 13:47

    Важно пометить функцию main() как async.


    1. itsoftmake Автор
      05.05.2015 13:56

      Да, обязательно, если мы хотим в ней использовать await.