Asynchronous programming: futures, async, await
This codelab teaches you how to write asynchronous code usingfutures and the async
and await
keywords. Using the embedded DartPadeditors, you can test your knowledge by running example code and completingexercises.
To get the most out of this codelab, you should have the following:
- Knowledge of basic Dart syntax.
- Some experience writing asynchronous code in another language.
This codelab covers the following material:
- How and when to use the
async
andawait
keywords. - How using
async
andawait
affects execution order. - How to handle errors from an asynchronous call using
try-catch
expressionsinasync
functions.
Estimated time to complete this codelab: 40-60 minutes.
备忘: This page uses embedded DartPads to display examples and exercises. If you see empty boxes instead of DartPads, go to theDartPad troubleshooting page.
如果你只看到了空白的框框(而没有任何内容),请查阅DartPad 常见问题页面。
Why asynchronous code matters
Asynchronous operations let your program complete work while waiting foranother operation to finish. Here are some common asynchronous operations:
- Fetching data over a network.
- Writing to a database.
- Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future
classand the async
and await
keywords.
Example: Incorrectly using an asynchronous function
The following example shows the wrong way to use an asynchronous function(getUserOrder()
). Later you’ll fix the example using async
and await
.Before running this example, try to spot the issue – what do you think theoutput will be?
getUserOrder()
eventuallyproduces:- getUserOrder()
is an asynchronous function that, after a delay,provides a string that describes the user’s order: a “Large Latte”.- To get the user’s order, createOrderMessage()
should call getUserOrder()
and wait for it to finish. Because createOrderMessage()
does not waitfor getUserOrder()
to finish, createOrderMessage()
fails to get the stringvalue that getUserOrder()
eventually provides.- Instead, createOrderMessage()
gets a representation of pending work to bedone: an uncompleted future. You’ll learn more about futures in the next section.- Because createOrderMessage()
fails to get the value describing the user’sorder, the example fails to print “Large Latte” to the console, and insteadprints “Your order is: Instance of ‘Future'".In the next sections you’ll learn the about futures, async
, and await
so that you’ll be able to write the code necessary to make getUserOrder()
print the desired value (“Large Latte”) to the console.Key terms:- synchronous operation: A synchronous operation blocks other operationsfrom executing until it completes.- synchronous function: A synchronous function only performs synchronousoperations.- asynchronous operation: Once initiated, an asynchronous operation allowsother operations to execute before it completes.- asynchronous function: An asynchronous function performs at least oneasynchronous operation and can also perform _synchronous operations.## What is a future?A future (lower case “f”) is an instance of the Future(capitalized “F”) class. A future represents the result of an asynchronousoperation, and can have two states: uncompleted or completed.备忘:Uncompleted is a Dart term referring to the state of a future before it has produced a value.### UncompletedWhen you call an asynchronous function, it returns an uncompleted future.That future is waiting for the function’s asynchronous operation to finish or tothrow an error.### CompletedIf the asynchronous operation succeeds, the future completes with avalue. Otherwise it completes with an error.#### Completing with a valueA future of type Future<T>
completes with a value of type T
.For example, a future with type Future<String>
produces a string value.If a future doesn’t produce a usable value, then the future’s type isFuture<void>
.#### Completing with an errorIf the asynchronous operation performed by the function fails for any reason, thefuture completes with an error.### Example: Introducing futuresIn the following example, getUserOrder()
returns a future that completes afterprinting to the console. Because it doesn’t return a usable value,getUserOrder()
has the type Future<void>
. Before you run the example,try to predict which will print first: “Large Latte” or “Fetching user order…”.In the preceding example, even though getUserOrder()
executes beforethe print()
call on line 8, the console shows the output from line 8(“Fetching user order…”) before the output from getUserOrder()
(“Large Latte”).This is because getUserOrder()
delays before it prints “Large Latte”.### Example: Completing with an errorRun the following example to see how a future completes with an error.A bit later you’ll learn how to handle the error.In this example, getUserOrder()
completes with an error indicating that theuser ID is invalid.You’ve learned about futures and how they complete, but how do you use theresults of asynchronous functions? In the next section you’ll learn how to getresults with the async
and await
keywords.Quick review:- A FutureT
.- If a future doesn’t produce a usable value, then the future’s type is Future<void>
.- A future can be in one of two states: uncompleted or completed.- When you call a function that returns a future, the function queues up work to be done and returns an uncompleted future.- When a future’s operation finishes, the future completes with a value or with an error.Key terms:-Future: the Dart Future class.-future: an instance of the Dart Future class.## Working with futures: async and awaitThe async
and await
keywords provide a declarative way to defineasynchronous functions and use their results. Remember these two basic guidelineswhen using async
and await
:- To define an async function, add async
before the function body:- The await
keyword works only in async
functions.Here’s an example that converts main()
from a synchronous to asynchronousfunction.First, add the async
keyword before the function body:You might have noticed that some functions (like
- main() async {
main()
, above) don’t have return types. That’s because Dart can infer the return type for you. Omitting return types is fine when you’re prototyping, but when you write production code, we recommend that you specify the return type.If the function has a declared return type, then update the type to beFuture<T>
, where T
is the type of the value that the function returns.If the function doesn’t explicitly return a value, then the return type isFuture<void>
:Now that you have an
- Future<void> main() async {
async
function, you can use the await
keyword to waitfor a future to complete:As the following two examples show, the
- print(await createOrderMessage());
async
and await
keywords result inasynchronous code that looks a lot like synchronous code.The only differences are highlighted in the asynchronous example, which — ifyour window is wide enough — is to the right of the synchronous example.#### Example: synchronous functions#### Example: asynchronous functions
- // SynchronousString createOrderMessage() { var order = getUserOrder(); return 'Your order is: $order';}Future<String> getUserOrder() { // Imagine that this function is // more complex and slow. return Future.delayed( Duration(seconds: 4), () => 'Large Latte');}// Synchronousmain() { print('Fetching user order…'); print(createOrderMessage());}// 'Fetching user order…'// 'Your order is: Instance of _Future<String>'
The asynchronous example is different in three ways:- The return type for
- // AsynchronousFuture<String> createOrderMessage() async { var order = await getUserOrder(); return 'Your order is: $order';}Future<String> getUserOrder() { // Imagine that this function is // more complex and slow. return Future.delayed( Duration(seconds: 4), () => 'Large Latte');}// Asynchronousmain() async { print('Fetching user order…'); print(await createOrderMessage());}// 'Fetching user order…'// 'Your order is: Large Latte'
createOrderMessage()
changes from String
to Future<String>
.- The async
keyword appears before the function bodies for createOrderMessage()
and main()
.- The await
keyword appears before calling the asynchronous functions getUserOrder()
and createOrderMessage()
.Key terms:- async: You can use the async
keyword before a function’s body to mark it asasynchronous.- async function: An async
function is a function labeled with the async
keyword.- await: You can use the await
keyword to get the completed result of anasynchronous expression. The await
keyword only works within an async
function.### Execution flow with async and awaitAn async
function runs synchronously until the firstawait
keyword. This means that within an async
function body, allsynchronous code before the first await
keyword executes immediately.备忘:Before Dart 2.0, an async
function returned immediately,without executing any code within the async
function body.### Example: Execution within async functionsRun the following example to see how execution proceeds within an async
function body. What do you think the output will be?After running the code in the preceding example, try reversing line 4 and line 5:Notice that timing of the output shifts, now that
- var order = await getUserOrder();print('Awaiting user order…');
print('Awaiting user order')
appears after the first await
keyword in createOrderMessage()
.### Exercise: Practice using async and awaitThe following exercise is a failing unit test that contains partially completedcode snippets. Your task is to complete the exercise by writing code to make thetests pass.You don’t need to implement main()
.To simulate asynchronous operations, call the following functions, which areprovided for you:Function | Type signature | Description |
---|---|---|
getRole() | Future<String> getRole() | Gets a short description of the user’s role. |
getLoginAmount() | Future<int> getLoginAmount() | Gets the number of times a user has logged in. |
reportUserRole()
function so that it does the following:- Returns a future that completes with the followingstring: "User role: <user role>"
- Note: You must use the actual value returned by getRole()
; copying andpasting the example return value won’t make the test pass. - Example return value: "User role: tester"
- Gets the user role by calling the provided function getRole()
.#### Part 2: reportLogins()Implement an async
function reportLogins()
so that it does the following:- Returns the string "Total number of logins: <# of logins>"
. - Note: You must use the actual value returned by getLoginAmount()
; copyingand pasting the example return value won’t make the test pass. - Example return value from reportLogins()
: "Total number of logins: 57"
- Gets the number of logins by calling the provided function getLoginAmount()
.
If your code passes the tests, you can ignore info-level messages.
Handling errors
To handle errors in an async
function, use try-catch:
try {
var order = await getUserOrder();
print('Awaiting user order...');
} catch (err) {
print('Caught error: $err');
}
Within an async
function, you can write try-catch clausesthe same way you would in synchronous code.
Example: async and await with try-catch
Run the following example to see how to handle an error from anasynchronous function. What do you think the output will be?
Exercise: Practice handling errors
The following exercise provides practice handling errors with asynchronous code,using the approach described in the previous section. To simulate asynchronousoperations, your code will call the following function, which is provided for you:
Function | Type signature | Description |
---|---|---|
getNewUsername() | Future<String> getNewUsername() | Returns the new username that you can use to replace an old one. |
Use async
and await
to implement an asynchronous changeUsername()
functionthat does the following:
- Calls the provided asynchronous function
getNewUsername()
and returns its result.- Example return value from
changeUsername()
:"jane_smith_92"
- Example return value from
- Catches any error that occurs and returns the string value of the error.
- You can use the toString() method to stringify both Exceptions and Errors.
Exercise: Putting it all together
It’s time to practice what you’ve learned in one final exercise.To simulate asynchronous operations, this exercise provides the asynchronousfunctions getUsername()
and logoutUser()
:
Function | Type signature | Description |
---|---|---|
getUsername() | Future<String> getUsername() | Returns the name associated with the current user. |
logoutUser() | Future<String> logoutUser() | Performs logout of current user and returns the username that was logged out. |
Write the following:
Part 1: addHello()
- Write a function
addHello()
that takes a single String argument. addHello()
returns its String argument preceded by ‘Hello ‘.Example:addHello('Jon')
returns'Hello Jon'
.
Part 2: greetUser()
- Write a function
greetUser()
that takes no arguments. - To get the username,
greetUser()
calls the provided asynchronousfunctiongetUsername()
. greetUser()
creates a greeting for the user by callingaddHello()
,passing it the username, and returning the result.Example: IfgetUsername()
returns'Jenny'
, thengreetUser()
returns'Hello Jenny'
.
Part 3: sayGoodbye()
- Write a function
sayGoodbye()
that does the following:- Takes no arguments.
- Catches any errors.
- Calls the provided asynchronous function
logoutUser()
.
- If
logoutUser()
fails,sayGoodbye()
returns any string you like. - If
logoutUser()
succeeds,sayGoodbye()
returns the string'<result> Thanks, see you next time'
, where<result>
isthe String value returned by callinglogoutUser()
.
What’s next?
Congratulations, you’ve finished the codelab! If you’d like to learn more, hereare some suggestions for where to go next:
- Play with DartPad.
- Try another codelab.
- Learn more about futures and asynchrony:
- Streams tutorial:Learn how to work with a sequence of asynchronous events.
- Dart videos from Google:Watch one or more of the videos about asynchronous coding.Or, if you prefer, read the articles that are based on these videos.(Start with isolates and event loops.)
- Get the Dart SDK.