Asynchronous programming allows a program to do work while waiting for something else to complete. In this tutorial, we'll show you how to work with Future in Dart (also in Flutter). At the end, you're gonna know:
- Introduction to Dart Future
- How to create Future, Future delayed and value methods
- Basic Future usage: then, catchError and callback
- Way to use Future async-await
- Future multiple await/then for chain of multiple asynchronous methods
- Equivalent of 'finally': Future whenComplete
- Future wait for multiple futures complete
This tutorial is originally from Bezkoder:
https://www.bezkoder.com/dart-future/
Dart Future overview
A Future represents a potential value (success), or error (fail), that will be available in the future. Generally we will need to work with a future value in following cases:
- getting data from a server
- querying data from a database
- reading a file
- doing a complicated computational work
Future<int> future = getFuture();
Future<Tutorial> tutorialsFut = getTutorialsFromDatabase(query);
Future<Data> dataFut = fetchDataFromServer(url);
Future<int> resultFut = computeBigData(value);
Dart Future then
We can use then()
to indicate code that runs when the future completes.
For example, fetchDataFromServer()
returns a Future
.
Future<Data> future = fetchDataFromServer(url);
future.then((Data data) {
print(data.values);
});
Dart Future callback
In the example above, we register a callback
that handles the value of a Future result. How about an error? We're gonna use catchError(callback)
.
For example:
Future<Data> future = fetchDataFromServer(url);
future.then((data) => handleData(data))
.catchError((error) => handleError(error));
Dart async-await
The async
and await
keywords provide a declarative way to define asynchronous function (that returns a Future
) and use its result.
We must remember 2 basic conditions:
- Asynchronous function has
async
before the function body. - The
await
keyword works only inasync
functions.
For example, we're gonna re-write the Future then above with async-await style.
main() async {
Data data = await fetchDataFromServer(url);
handleData(data);
}
Furthermore, then-catchError is equivalent to async-await like this:
main() async {
try {
Data data = await fetchDataFromServer(url);
handleData(data);
} catch (error) {
handleError(error);
}
}
Let's continue with some simple ways to create a Future
.
Dart Future delayed
Future<T>.delayed(Duration duration, [computation()?])
delayed()
method creates a Future
that runs computation
after the given duration has passed, and the future is completed with the result of the computation
.
If the duration
is 0
or less, it completes no sooner than in the next event-loop iteration, after all microtasks have run.
For example:
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);
Output:
(after 2 seconds)
Delayed 2 seconds.
Dart Future value
Future<T>.value(value)
The method Future.value()
creates a Future completed with value
.
For example:
Future.value("bezkoder.com")
.then((result) => print(result));
async-await:
var result = await Future.value("bezkoder.com");
print(result);
Output:
bezkoder.com
Dart Future error
Future.error(Object error)
creates a future that completes with an error
.
For example:
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);
}
Output:
> This is an error message!
Write asynchronous Function with Future
Now we create a asynchronous function that returns a Future
. The function receives an input int
number. If the input number is less than 0
, it completes with a Future error, or else, completes after given time and return the input number.
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;
});
}
Let's use doInSeconds()
function.
doInSeconds(2).then((result) {
print(result);
}).catchError((error) {
print('> inside catchError');
print(error);
});
Or:
try {
var result = await doInSeconds(2);
print(result);
} catch (error) {
print(error);
}
Output:
> Processing with value=2...
(after 2 seconds)
> Something done in 2 second(s).
2
Check the catching Error:
try {
var result = await doInSeconds(-1);
print(result);
} catch (error) {
print('> inside catchError');
print(error);
}
Output:
> Processing with value=-1...
> inside catchError
> input 'sec' must be greater than 0
Chain of multiple asynchronous methods in Dart
This is how we handle the case in that we need to call run multiple asynchronous functions in a certain order.
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 + 1))
.then((v2) => doInSeconds(v2 + 1))
.catchError((error) => print(error));
Output:
> 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).
- If the callback inside
then()
returns a Future,then()
returns a Future that will complete with the same result. - If the callback returns a value of any other type,
then()
creates a new Future that completes with the value.
Dart multiple await
Let's run multiple asynchronous functions in chain using async-await:
try {
final v1 = await doInSeconds(1);
final v2 = await doInSeconds(v1 + 1);
await doInSeconds(v2 + 1);
} catch (error) {
print(error);
}
Output:
> 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
We've known that then-catchError
is equivalent to a try-catch
, so whenComplete
is for finally
.
The callback inside whenComplete()
is called after all, whether the result is with a value or with an 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!");
}
equivalent:
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 + 1))
.then((v2) => doInSeconds(v2 + 1))
.catchError((error) => print(error))
.whenComplete(() => print("Done!"));
Output:
> 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!
Check another code that will cause an error.
Future future = doInSeconds(1);
future
.then((v1) => doInSeconds(v1 - 1))
.then((v2) => doInSeconds(v2 - 1))
.catchError((error) => print(error))
.whenComplete(() => print("Done!"));
Output:
> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=0...
> input 'sec' must be greater than 0
Done!
Future wait in Dart
If you need to run many asynchronous functions and wait for them all to complete before continuing something else, you can use static method: Future.wait()
to manage multiple Futures:
var query = doInSeconds;
var compute = doInSeconds;
var work = doInSeconds;
await Future.wait([
query(1),
compute(6),
work(3),
]);
print('Done!');
Output:
> 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!
Conclusion
In this tutorial, we've learned overview of a Dart/Flutter Future, how to create a Future simply using delayed or value method, how to work with then-catchError-whenComplete or try-catch-finally with Future async-await, way to handle chain of multiple asynchronous methods or Future wait for multiple futures complete.
Happy learning! See you again!