Summary: in this tutorial, you’ll learn about Dart Future and how to use it to handle asynchronous tasks.
Introduction to the Dart Future
A future is an object that represents the result of an asynchronous operation. A future object will return a value at a later time.
A future has two states: uncompleted and completed. When a future completes, it has two possibilities:
- Completed with a value
- Failed with an error
Dart uses the Future<T>
class for creating future objects. Because the Future<T>
is a generic class, the value it returns can be any object.
In practice, you often consume future objects instead of creating them.
Dart Future example
The following example uses the Future.delayed()
constructor to create a future object that returns the number 2 after one second:
var future = Future<int>.delayed(
Duration(seconds: 1),
() => 2
);
Code language: Dart (dart)
In this example:
- The
future
is an object of theFuture<int>
class. - The first argument of the
Future<int>.delayed()
constructor is aDuration
object. Dart will add the anonymous function in the second argument to the event queue and delay executing it by one second. - The event loop will move the anonymous function
() => 2
to the main isolate and execute it.
When a future completes, you can get the return value. There are two ways to get the value after the future completes:
- Using a callback
- Using
async
andawait
keywords
Using callbacks
When a future completes, you can run a callback to handle the result. The Future<T>
class provides three methods for scheduling a callback:
then()
catchError()
whenComplete()
If a future completes successfully with a value, you can get the result by adding a callback to the then()
method.
If a future fails with an error, you can handle the error in the catchError()
method.
Whether a future completes with a value or an error, you can schedule a callback in the whenComplete()
method.
In other words, the callback that you pass to the whenComplete()
method will always execute whether the future succeeds or not.
The following example illustrates how to use the above using the callback:
void main() {
print(1);
var future = Future<int>.delayed(Duration(seconds: 1), () => 2);
future.then((value) => print(value));
print(3);
}
Code language: Dart (dart)
Output:
1
3
2
Code language: Dart (dart)
In this example, Dart executes the print(1)
, creates a future object, and executes the print(3)
sequentially.
However, when Dart encounters the future object, it adds the following anonymous function to the event queue:
() => 2
Code language: Dart (dart)
Once Dart completes executing the print(3)
statement, the event loop moves the anonymous function to the main isolate for execution.
Because we schedule a callback in the then()
method that prints the value returned by the future, we see the number 2 appears after the number 3 in the output after one second.
The following example changes the delay from one to zero seconds in the duration of the Future
object:
void main() {
print(1);
var future = Future<int>.delayed(Duration(seconds: 0), () => 2);
future.then((value) => print(value));
print(3);
}
Code language: Dart (dart)
Output:
1
3
2
Code language: Dart (dart)
In this example, the number 2 appears immediately after the number 3 because the future has zero delays.
Using async and await keywords
The async
and await
keywords provide a declarative way to define asynchronous functions.
When using async
and await
keywords, you should follow these rules:
- Place the
async
keyword before the function body to make the function asynchronous. - The
await
keyword works only inasync
functions. In other words, if a function containsawait
keywords, you need to make it anasync
function.
The following example converts main()
function from a synchronous to asynchronous function:
Future<void> main() async {
print(1);
var value = await Future<int>.delayed(
Duration(seconds: 0),
() => 2
);
print(value);
print(3);
}
Code language: Dart (dart)
Output:
1
2
3
Code language: Dart (dart)
In this example:
- First, add the
async
keyword to themain()
function to make it asynchronous. Since themain()
function doesn’t explicitly return a value, its return type isFuture<void>
. - Second, add the
await
keyword in front of the future object. Once Dart encounters theawait
keyword, it sends all the code starting from that line to the event queue and waits for the future to complete. That’s why you see the number 2 before 3.
Handling errors with try-catch block
If an asynchronous operation results in an error, you can use the try-catch block to handle it. For example:
Future<void> main() async {
print(1);
try {
var value = await Future<int>.delayed(
Duration(seconds: 0),
() => throw Exception('An error from the future'));
print(value);
} catch (e) {
print(e);
}
print(3);
}
Code language: Dart (dart)
Output:
1
Exception: Error
3
Code language: Dart (dart)
In this example, the Future
object raises an exception. And we use the try-catch
statement to catch it and display the error message.
Dart future practical example
The following example reads the contents of a text file and prints it out:
import 'dart:io';
Future<void> main() async {
try {
var file = File('readme1.txt');
var contents = await file.readAsString();
print(contents);
} on FileSystemException catch (e) {
print(e);
}
}
Code language: Dart (dart)
How it works.
First, import the dart:io
library:
import 'dart:io';
Code language: Dart (dart)
Second, create a File
object with a file path:
var file = File('readme.txt');
Code language: Dart (dart)
Third, read the contents of a file using the readAsString()
method. The method returns a Future<String>
which is the contents of the text file:
var contents = await file.readAsString();
Code language: Dart (dart)
If the read is successful, display the text file’s contents:
print(contents);
Code language: Dart (dart)
If the file is not found or an error while reading a file, display the error message in the catch
block.
Summary
- A future is an object that represents the result of an asynchronous operation.
- A future completes successfully with a value or fails with an error.
- Dart uses the
Future<T>
class for creating future objects. - Use the
async
keyword to define asynchronous functions. - Use the
await
keyword to wait for a future to complete.