Dart code runs in a single “thread” of execution.
Code that blocks the thread of execution can make your program freeze.
Future
objects (
futures
) represent the results of
asynchronous operations
— processing or I/O to be completed later.
To suspend execution until a future completes,
use
await
in an async function (or use
then()
).
To catch errors, use try-catch expressions in async functions
(or use
catchError()
).
To run code concurrently, create an
isolate
(or for a web app, a
worker
).
Dart code runs in a single “thread” of execution.
If Dart code blocks — for example, by performing a
long-running calculation or waiting for I/O — the entire program freezes.
Asynchronous operations let your program complete other work while
waiting for an operation to finish.
Dart uses
Future
objects (futures) to represent the results of
asynchronous operations.
To work with futures,
you can use either
async
and
await
or the
Future
API.
Note:
All Dart code runs in the context of an
isolate
that
owns all of the memory that the Dart code uses.
While Dart code is executing, no other code in the same isolate can run.
If you want multiple parts of Dart code to run concurrently,
you can run them in separate isolates.
(Web apps use workers instead of isolates.)
Multiple isolates run at the same time, usually each on its own CPU core.
Isolates don’t share memory, and the only way they can interact
is by sending messages to each other.
For more information, see the documentation for
isolates
or
web workers.
Introduction
Let’s look at some code that might cause a program to freeze:
// Synchronous code
void printDailyNewsDigest() {
var newsDigest = gatherNewsReports(); // Can take a while.
print(newsDigest);
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
Our program gathers the news of the day, prints it, and then prints a bunch of
other items of interest to the user:
<gathered news goes here>
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
Our code is problematic: since gatherNewsReports()
blocks, the remaining
code runs only after gatherNewsReports()
returns with the contents of the
file, however long that takes. If reading the file takes a long time,
the user has to wait, wondering if they won the lottery, what tomorrow’s
weather will be, and who won today’s game.
To help keep the application responsive, Dart library authors use an
asynchronous model when defining functions that do potentially expensive work.
Such functions return their value using a future.
What is a future?
A future is a Future<T>
object, which
represents an asynchronous operation that produces a result of type T
.
If the result isn’t a usable value, then the future’s type is Future<void>
.
When a function that returns a future is invoked, two things happen:
The function queues up work to be done and returns an uncompleted Future
object.
Later, when the operation is finished, the Future
object
completes with a value or with an error.
When writing code that depends on a future, you have two options:
Use async
and await
Use the Future
API
Async and await
The async
and await
keywords are part of the Dart language’s asynchrony
support. They allow you
to write asynchronous code that looks like synchronous code and doesn’t use
the Future
API. An async function is one that has the async
keyword
before its body. The await
keyword works only in async functions.
Version note: In Dart 1.x, async functions immediately suspended
execution. In Dart 2, instead of immediately suspending, async functions
execute synchronously until the first await
or return
.
The following app simulates reading the news by using async
and await
to read the contents of a file on this site.
Click run
to start the app.
Or open a
DartPad window containing the app,
run the app, and click CONSOLE to see the app’s output.
Notice that printDailyNewsDigest()
is the first function called, but the news
is the last thing to print, even though the file contains only a single
line. This is because the code that reads and prints the file is running
asynchronously.
In this example, the printDailyNewsDigest()
function calls
gatherNewsReports()
, which is non-blocking. Calling gatherNewsReports()
queues up the work to be done but doesn’t stop the rest of the code from
executing. The program prints the lottery numbers, the forecast, and the
baseball score; when gatherNewsReports()
finishes gathering news, the
program prints. If gatherNewsReports()
takes a little while to complete its
work, no great harm is done: the user gets to read other things before the
daily news digest is printed.
Note the return types. The return type of gatherNewsReports()
is
Future<String>
, which means that it returns a future that completes with
a string value. The printDailyNewsDigest()
function, which doesn’t
return a value, has the return type Future<void>
.
The following diagram shows the flow of execution through the code. Each
number corresponds to a step below.
The app begins executing.
The main()
function calls the async function printDailyNewsDigest()
,
which begins executing synchronously.
printDailyNewsDigest()
uses await
to call the function
gatherNewsReports()
, which begins executing.
The gatherNewsReports()
function returns an uncompleted
future (an instance of Future<String>
).
Because printDailyNewsDigest()
is an async function and is
awaiting a value, it pauses its execution and returns an uncompleted
future (in this case, an instance of Future<void>
)
to its caller (main()
).
The remaining print functions execute. Because they’re synchronous, each
function executes fully before moving on to the next print function. For
example, the winning lottery numbers are all printed before the weather
forecast is printed.
When main()
has finished executing, the asynchronous functions can resume
execution. First, the future returned by gatherNewsReports()
completes.
Then printDailyNewsDigest()
continues executing, printing the news.
When the printDailyNewsDigest()
function body finishes executing,
the future that it originally returned completes, and the app exits.
Note that an async function starts executing right away (synchronously).
The function suspends execution and returns an uncompleted future
when it reaches the first occurrence of any of the following:
The function’s first await
expression (after the function
gets the uncompleted future from that expression).
Any return
statement in the function.
The end of the function body.
Handling errors
If a Future
-returning function completes with an error, you probably want to
capture that error. Async functions can handle errors using try-catch:
Future<void> printDailyNewsDigest() async {
try {
var newsDigest = await gatherNewsReports();
print(newsDigest);
} catch (e) {
// Handle error...
The try-catch code behaves in the same way with asynchronous code as it does
with synchronous code: if the code within the try
block throws an exception,
the code inside the catch
clause executes.
Sequential processing
You can use multiple await
expressions to ensure that each statement
completes before executing the next statement:
// Sequential processing using async and await.
main() async {
await expensiveA();
await expensiveB();
doSomethingWith(await expensiveC());
The expensiveB()
function doesn’t execute until expensiveA()
has
finished, and so on.
Before async
and await
were added in Dart 1.9,
you had to use the Future
API.
You might still see the Future
API used in older code and in code that needs
more functionality than async-await offers.
To write asynchronous code using the Future
API, you use the then()
method
to register a callback. This callback fires when the Future
completes.
The following app simulates reading the news by using the Future
API to read
the contents of a file on this site.
Click run
to start the app.
Or open a
DartPad window containing the app,
run the app, and click CONSOLE to see the app’s output.
Notice that printDailyNewsDigest()
is the first function called, but the news
is the last thing to print, even though the file contains only a single
line. This is because the code that reads the file is running asynchronously.
This app executes as follows:
The app begins executing.
The main function calls the printDailyNewsDigest()
function, which does
not return immediately, but calls gatherNewsReports()
.
gatherNewsReports()
starts gathering news and returns a Future
.
printDailyNewsDigest()
uses then()
to specify a response to the
Future
. Calling then()
returns a new Future
that will complete with the
value returned by then()
’s callback.
The remaining print functions execute. Because they’re synchronous, each
function executes fully before moving on to the next print function. For
example, the winning lottery numbers are all printed before the weather
forecast is printed.
When all of the news has arrived, the Future
returned by
gatherNewsReports()
completes with a string containing the gathered news.
The code specified by then()
in printDailyNewsDigest()
runs, printing
the news.
The app exits.
Note:
In the printDailyNewsDigest()
function, the code future.then(print)
is equivalent to the following:
future.then((newsDigest) => print(newsDigest))
Alternatively, the code inside then()
can use curly braces:
Future<void> printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then((newsDigest) {
print(newsDigest);
// Do something else...
You need to provide an argument to then()
’s callback,
even if the Future
is of type Future<void>
.
By convention, an unused argument is named _
(underscore).
final future = printDailyNewsDigest();
return future.then((_) {
// Code that doesn't use the `_` parameter...
print('All reports printed.');
Handling errors
With the Future
API, you can capture an error using catchError()
:
Future<void> printDailyNewsDigest() =>
gatherNewsReports().then(print).catchError(handleError);
If the news stream isn’t available for reading, the
code above executes as follows:
The future returned by gatherNewsReports()
completes with an error.
The future returned by then()
completes with an error;
print()
isn’t called.
The callback for catchError()
(handleError()
) handles the error,
the future returned by catchError()
completes normally,
and the error does not propagate.
Chaining catchError()
to then()
is a common pattern
when using the Future
API.
Consider this pairing the Future
API’s equivalent of try-catch blocks.
Like then()
, catchError()
returns a new Future
that completes with the
return value of its callback.
For more details and examples, read Futures and Error Handling.
Calling multiple functions that return futures
Consider three functions, expensiveA()
, expensiveB()
, and expensiveC()
,
that return Future
objects. You can invoke them sequentially (one function starts
when a previous one completes), or you can kick off all of them at the same
time and do something once all the values return. The Future
interface is
fluid enough to deal with both use cases.
Chaining function calls using then()
When Future
-returning functions need to run in order, use chained then()
calls:
expensiveA()
.then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));
Nested callbacks also work, but they’re harder to read and not as Dart-y.
Waiting on multiple futures to complete using Future.wait()
If the order of execution of the functions is not important, you can use
Future.wait()
.
When you pass Future.wait()
a list of futures,
it immediately returns a Future
. That future doesn’t complete until
all of the given futures have completed.
Then it completes with a list containing the values produced by
each future in the original list.
Future.wait([expensiveA(), expensiveB(), expensiveC()])
.then((List responses) => chooseBestResponse(responses, moreInfo))
.catchError(handleError);
If any of the invoked functions completes with an error, the Future
returned
by Future.wait()
also completes with an error. Use catchError()
to handle
the error.
Other resources
Read the following documentation for more details on using futures and
asynchronous programming in Dart:
Futures and Error Handling, an article that starts where this tutorial
ends.
The Event Loop and Dart,
an article that describes how to schedule tasks using futures.
Asynchrony support,
a section in the language tour.
API reference documentation for
futures,
isolates, and
web workers.