Учебник Dart Future с примерами
Асинхронное программирование позволяет программе выполнять работу, ожидая завершения чего-то другого. В этом уроке мы покажем вам, как работать с Future в Dart (также с Flutter). В конце концов ты узнаешь:
- Введение в Dart Future
- Как создать методы Future, Future delayed и value
- Основное использование Future: then, catchError и callback
- Способ использования Future async-await
- Future multiple await/then for chain of multiple асинхронных методов
- Эквивалент «наконец-то»: Future whenComplete
- Future wait для завершения нескольких фьючерсов
Обзор Dart Future
Future представляет собой потенциальное значение (success), или ошибку (fail), которые будут доступны в будущем. Как правило нам нужно будет работать с будущей стоимостью в следующих случаях:
- получение данных с сервера
- запрос данных из базы данных
- чтение файла
- выполнение сложной вычислительной работы
Future<int> future = getFuture();
Future<Tutorial> tutorialsFut = getTutorialsFromDatabase(query);
Future<Data> dataFut = fetchDataFromServer(url);
Future<int> resultFut = computeBigData(value);
Dart Future then
Мы можем использовать then()
, чтобы указать код, который выполняется после завершения future.
Для примера, fetchDataFromServer()
возвращает значение Future
.
Future<Data> future = fetchDataFromServer(url);
future.then((Data data) {
print(data.values);
});
Dart Future callback
В приведенном выше примере мы регистрируем callback
, который обрабатывает значение Future результата. Как насчет ошибки? Мы собираемся использовать catchError(callback)
.
Например:
Future<Data> future = fetchDataFromServer(url);
future.then((data) => handleData(data))
.catchError((error) => handleError(error));
Dart async-await
Ключевые слова async
и await
предоставляют декларативный способ определения асинхронной функции (которая возвращает Future
) и использования ее результата.
Мы должны помнить два основных условия:
- Асинхронная функция имеет
async
перед телом функции - Ключевое слово
await
работает только вasync
функции
Например, мы собираемся переписать Future с использованием стиля async-await.
main() async {
Data data = await fetchDataFromServer(url);
handleData(data);
}
Кроме того, then-catchError эквивалентен async-await следующим образом:
main() async {
try {
Data data = await fetchDataFromServer(url);
handleData(data);
} catch (error) {
handleError(error);
}
}
Давайте продолжим с некоторыми простыми способами создания:
Dart Future delayed
Future<T>.delayed(Duration duration, [computation()?])
Метод delayed()
создает Future
, который запускает вычисления по истечении заданного времени, и future завершается с результатом вычисления.
Если duration
равно 0
или меньше, она завершается не раньше, чем на следующей event-loop итерации, после выполнения всех микрозадач.
Например:
Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.')
.then((result) => print(result));
async-await:
var result = await Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.');
print(result);
Вывод:
(after 2 seconds)
Delayed 2 seconds.
Dart Future value
Future<T>.value(value)
Метод Future.value()
создает Future дополненное с value
.
Например:
Future.value("bezkoder.com")
.then((result) => print(result));
async-await:
var result = await Future.value("bezkoder.com");
print(result);
Вывод:
bezkoder.com
Dart Future error
Future.error(Object error)
создает future, которое завершается с error
.
Например:
Future.error('> This is an error message!')
.catchError((err) => print(err));
async-await:
try {
await Future.error('> This is an error message!');
} catch (err) {
print(err);
}
Вывод:
> This is an error message!
Напишите асинхронную функцию с Future
Теперь мы создадим асинхронную функцию, которая возвращает Future
. Функция получает входной номер int
. Если введенное число меньше 0
, оно завершается ошибкой Future, или же завершается через заданное времяи возвращает введенное число.
Future<int> doInSeconds(int sec) {
print('> Processing with value=${sec}...');
if (sec <= 0) {
return Future.error('> input \'sec\' must be greater than 0');
}
return Future.delayed(Duration(seconds: sec), () {
print('> Something done in ${sec} second(s).');
return sec;
});
}
Давайте воспользуемся функцией doInSeconds()
doInSeconds(2).then((result) {
print(result);
}).catchError((error) {
print('> inside catchError');
print(error);
});
или
try {
var result = await doInSeconds(2);
print(result);
} catch (error) {
print(error);
}
Вывод:
> Processing with value=2...
(after 2 seconds)
> Something done in 2 second(s).
2
Проверьте Error:
try {
var result = await doInSeconds(-1);
print(result);
} catch (error) {
print('> inside catchError');
print(error);
}
Вывод:
> Processing with value=-1...
> inside catchError
> input 'sec' must be greater than 0
Цепочка из нескольких асинхронных методов в Dart
Вот так мы справляемся со случаем, когда нам нужно вызвать несколько асинхронных функций в определенном порядке.
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 + 1))
.then((v2) => doInSeconds(v2 + 1))
.catchError((error) => print(error));
Вывод:
> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
- Если обратный вызов внутри
then()
возвращает Future, тоthen()
возвращает Future, который завершится тем же результатом. - Если обратный вызов возвращает значение любого другого типа, то
then()
создает новый Future, который завершается value.
Dart multiple await
Давайте запустим multiple асинхронных функций в цепочке, используя async-await:
try {
final v1 = await doInSeconds(1);
final v2 = await doInSeconds(v1 + 1);
await doInSeconds(v2 + 1);
} catch (error) {
print(error);
}
Вывод:
> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
Dart Future whenComplete
Мы знаем, что then-catchError
эквивалентен try-catch
, так что whenComplete
используется для finally
.
Обратный вызов внутри whenComplete()
вызывает после все, независимо от того, является ли результат value или error.
try-catch-finally:
try {
final v1 = await doInSeconds(1);
final v2 = await doInSeconds(v1 + 1);
await doInSeconds(v2 + 1);
} catch (error) {
print(error);
} finally {
print("Done!");
}
эквивалент:
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 + 1))
.then((v2) => doInSeconds(v2 + 1))
.catchError((error) => print(error))
.whenComplete(() => print("Done!"));
Вывод:
> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
Done!
Проверьте другой код, который вызовет ошибку.
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 - 1))
.then((v2) => doInSeconds(v2 - 1))
.catchError((error) => print(error))
.whenComplete(() => print("Done!"));
Вывод:
> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=0...
> input 'sec' must be greater than 0
Done!
Future ждать в Dart
Если вам нужно запустить много асинхронных функций и дождаться их завершения, прежде чем продолжить что-то еще, вы можете использовать статический метод: Future.wait()
для управления несколькими фьючерсами.
var query = doInSeconds;
var compute = doInSeconds;
var work = doInSeconds;
await Future.wait([
query(1),
compute(6),
work(3),
]);
print('Done!');
Вывод:
> Processing with value=1...
> Processing with value=6...
> Processing with value=3...
> Something done in 1 second(s).
> Something done in 3 second(s).
> Something done in 6 second(s).
Done!
Вывод
В этом руководстве мы изучили обзор Dart/Flutter Future, как создать Future, просто используя методы delayed или value, как работать с then-catchError-whenComplete или try-catch-finally с Future async-await, как для обработки цепочки multiple асинхронных методов или ожидания завершения нескольких фьючерсов.