Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async await codelab #1659

Merged
merged 51 commits into from
Aug 7, 2019
Merged
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7cf3303
Add WIP async await codelab
legalcodes Jun 4, 2019
cd75e7f
Limit lines to 80 chars
legalcodes Jun 4, 2019
33a671b
Round 1 of revisions:
legalcodes Jun 5, 2019
3934ee6
Add more updates per language comments/suggestions review feedback
legalcodes Jun 5, 2019
b34a5d7
Further small updates addressing review comments.
legalcodes Jun 5, 2019
b587b7f
Configure site.alert templates and update futures codelab w/ templates
legalcodes Jun 6, 2019
7396cc6
Add "Practicing" section iframe; slight restructure for "Errors" section
legalcodes Jun 6, 2019
0e169e0
Added dartpad examples
legalcodes Jun 10, 2019
d2bafa7
Remove extra title
legalcodes Jun 11, 2019
846944c
Remove section head and use of "future object"
legalcodes Jun 11, 2019
60374bf
Remove typos
legalcodes Jun 11, 2019
48f7c3a
Merge branch 'master' of https://github.com/dart-lang/site-www into f…
legalcodes Jun 11, 2019
0fd4ed2
Punctuate all bulleted list items.
legalcodes Jun 12, 2019
4417dd9
Allow check-code to fail for dart dev builds
legalcodes Jun 12, 2019
11427cf
Correct mistaken exclusion of check-code from builds
legalcodes Jun 12, 2019
a63570b
Allow failure for check-code.sh
legalcodes Jun 12, 2019
38b3d5b
Merge branch 'master' of https://github.com/dart-lang/site-www into f…
legalcodes Jun 12, 2019
5f9d525
Allow failure for both check-code and analyze-and-test-examples
legalcodes Jun 12, 2019
c0c98ae
Updates per ux and tw review
legalcodes Jun 19, 2019
5ff8fb1
More updates per review from @sfshaza2
legalcodes Jun 19, 2019
d18e711
Updates per latest sfshaza2 comments
legalcodes Jul 9, 2019
71b621c
Merge branch 'master' of https://github.com/dart-lang/site-www into f…
legalcodes Jul 9, 2019
0c92c5c
Incorporate first part of changes from UX round 2
legalcodes Jul 11, 2019
9d136d3
Merge branch 'master' of https://github.com/dart-lang/site-www into f…
legalcodes Jul 15, 2019
3120d87
Correct typos, turn on latest DartPad UI, and misc small fxies
legalcodes Jul 16, 2019
123f576
Update descriptions for examples
legalcodes Jul 16, 2019
f67ffc4
Add updates from observing test user
legalcodes Jul 18, 2019
5e5ec20
Minor update for example missing a border, extra sentence for DartPad
legalcodes Jul 19, 2019
125f9a3
Re-organize introduction per latest feedback:
legalcodes Jul 20, 2019
4ee31a2
Remove "!" characters from "Putting it all together" section
legalcodes Jul 24, 2019
64dc921
Adding changes from latest review
legalcodes Jul 30, 2019
da8e3e3
Refactored sync / async comparison section into 2 columns
legalcodes Jul 31, 2019
95a7ab7
Updated instructions before each example
legalcodes Jul 31, 2019
c151e1d
Add changes before switching markup back to single-column view
legalcodes Aug 1, 2019
86367c6
Completed markup re-write
legalcodes Aug 2, 2019
d7b72c0
Finished updates from latest review comments
legalcodes Aug 2, 2019
0e6a5a9
End with new lines for .travis.yml and .firebaserc
legalcodes Aug 2, 2019
cc371e2
Remove dartpad includes file, update nav per review comments
legalcodes Aug 2, 2019
60a0d36
Update codelab title
legalcodes Aug 2, 2019
f48f067
Incorporate suggestions from latest review
legalcodes Aug 2, 2019
2c8c32f
Enforce 80 char line limit
legalcodes Aug 2, 2019
9621eb3
Small changes from latest review
legalcodes Aug 5, 2019
e0aa7b8
Updates from latest review
legalcodes Aug 6, 2019
f672a1a
Test omitting refresh-code-excerpts.sh - failing built
legalcodes Aug 6, 2019
b2a44ce
Remove test fix for build
legalcodes Aug 6, 2019
cdda574
Some changes based on recent review
legalcodes Aug 6, 2019
37b6c04
Incorporate latest review items
legalcodes Aug 6, 2019
6624930
Remove 'before running it'
legalcodes Aug 6, 2019
fb19d9c
Exercise applies to "Working with futures"
legalcodes Aug 7, 2019
f48d9c3
More updates - latest review
legalcodes Aug 7, 2019
218c301
Add survey at the end of the codelab
legalcodes Aug 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 69 additions & 49 deletions src/codelabs/async-await/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
title: "Asynchrony: futures, async, await"
description: Learn about and practice writing asynchronous code in DartPad!
---
<!-- TODO: ensure 80chars -->
This codelab teaches you how to write asynchronous code using
futures and the `async` and `await` keywords. This codelab includes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting two sentences in a row with "This codelab" isn't ideal. My original suggestion was this:

This codelab teaches you how to write asynchronous....
Using the embedded DartPad editors, you can test your...

embedded editors that you can use to test your knowledge by running
example code and completing exercises.

To get the most out of this codelab, you should have the following:
* [basic Dart syntax](/codelabs/dart-cheatsheet).
* [Basic Dart syntax](/codelabs/dart-cheatsheet).
* Some experience writing asynchronous code in another language.

This codelab covers the following material:
Expand Down Expand Up @@ -55,8 +54,8 @@ produces:
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* wait
for `getUserOrder()` to finish, `createOrderMessage()` fails to get the string value
that `getUserOrder()` eventually provides.
for `getUserOrder()` to finish, `createOrderMessage()` fails to get the string
value that `getUserOrder()` eventually provides.
* Instead, `createOrderMessage()` gets a representation of pending work to be
done: an uncompleted future. You'll learn more about futures in the next section.
* Because `createOrderMessage()` fails to get the value describing the user's
Expand All @@ -68,18 +67,15 @@ so that you'll be able to write the code necessary to make `getUserOrder()`
print the desired value ("Large Latte") to the console.

{{ site.alert.secondary }}
**Key terms:**

* **synchronous operation**: A synchronous operation blocks other operations from executing until it completes.

* **synchronous function**: A synchronous function only performs synchronous operations.

* **asynchronous operation**: Once initiated, an asynchronous operation allows
other operations to execute before it completes.

* **asynchronous function**: An asynchronous function performs at least one
asynchronous operation and can also perform _synchronous_ operations.

**Key terms:**
* **synchronous operation**: A synchronous operation blocks other operations
from executing until it completes.
* **synchronous function**: A synchronous function only performs synchronous
operations.
* **asynchronous operation**: Once initiated, an asynchronous operation allows
other operations to execute before it completes.
* **asynchronous function**: An asynchronous function performs at least one
asynchronous operation and can also perform _synchronous_ operations.
{{ site.alert.end }}


Expand All @@ -96,7 +92,8 @@ operation, and can have two states: uncompleted or completed.
### Uncompleted

When you call an asynchronous function, it returns an uncompleted future.
That future is waiting for the function's asynchronous operation to finish or to throw an error.
That future is waiting for the function's asynchronous operation to finish or to
throw an error.

### Completed
If the asynchronous operation succeeds, the future completes with a
Expand All @@ -106,7 +103,8 @@ value. Otherwise it completes with an error.

A 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 is `Future<void>`.
If a future doesn't produce a usable value, then the future's type is
`Future<void>`.

#### Completing with an error

Expand All @@ -115,10 +113,10 @@ future completes with an error.

### Example: Introducing futures
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "Introducing futures" the best name? (not that I have a better suggestion, but this title seems very generic)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to riff on other possible names:

Example: 
1. Fun with Futures
2. Futures Fundamentals
3. The basics of Futures
4. Facts about Futures
5. Back to the Futures - this would require re-working the example to use a lot of "Maaaartty" or "MCFLY!!" strings. Why didn't we notice this sooner? I regret everything.


In the following example, `getUserOrder()` returns a future that completes after printing
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 following example, `getUserOrder()` returns a future that completes after
printing 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..." ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"try.....?" -> should be end with a period.


[//]: https://gist.github.com/57e6085344cbd1719ed42b32f8ad1bce
<iframe
Expand Down Expand Up @@ -147,20 +145,23 @@ How do you think you might handle the error?
width="100%" >
</iframe>

In this example, `getUserOrder()` completes with an error indicating that the user ID is invalid.
In this example, `getUserOrder()` completes with an error indicating that the
user ID is invalid.

You've learned about futures and how they complete, but how do you use the results
of asynchronous functions? In the next section you'll learn how to get results with
the `async` and `await` keywords.
You've learned about futures and how they complete, but how do you use the
results of asynchronous functions? In the next section you'll learn how to get
results with the `async` and `await` keywords.

{{ site.alert.secondary }}
**Quick review:**
* A [Future\<T\>][future class] instance produces a value of type `T`.
* If a future doesn't produce a usable value, then the future's type is `Future<void>`.
* 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.
* When a future's operation finishes, the future completes with a value or
with an error.

**Key terms:**
* **Future**: the Dart [Future][future class] class.
Expand All @@ -176,23 +177,27 @@ when using `async` and `await`:
* __The `await` keyword works only in `async` functions.__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse the order of these two lines? In the following part, you first talk about async and then await.

* __To define an async function, add `async` before the function body:__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove colon(:)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I noticed that @kwalrath suggested to add this colon. Hi Kathy, it's a little confusing to me to have this colon at the end of a list

    . Also, the content below this list seem to include two points.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The colon isn't supposed to be in the list, it's supposed to be in the intro to the sample.


Here's an example that converts `main()` from a synchronous to asynchronous function.
Here's an example that converts `main()` from a synchronous to asynchronous
function.

First, add the `async` keyword before the function body.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

body. -> body:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this change. The style guide favors using a colon if the intro immediately precedes the sample (https://developers.google.com/style/code-samples#intros).

{% prettify dart %}
main() [!async!] {
{% endprettify %}

If needed, update the function's type signature to return a future.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If needed" doesn't seem quite right here. Maybe:

If the function has a declared return type, then update the type to be Future<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 is Future<void>:

It's kind of long, but people can skip it if they get the concept.

If the function has a declared return type, then update the type to be `Future<T>`,
If the function has a declared return type, then update the type to be
`Future<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 is `Future<void>`:
If the function doesn't explicitly return a value, then the return type is
`Future<void>`:

{% prettify dart %}
[!Future<void>!] main() async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little confusing to me that you added Future before the main() but didn't include this in the later side-by-side example.

The content says "If needed, update the function’s type signature". This makes me wonder "so why did you add a function’s type signature here but didn't add a function’s type signature in "Example: asynchronous functions".

Copy link
Contributor Author

@legalcodes legalcodes Aug 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing the If needed, update the function's type signature sentence.

Copy link
Contributor Author

@legalcodes legalcodes Aug 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with your point about you added Future before the main() but didn't include this in the later side-by-side example., which is why I think main() might not be the best choice for this example. @kathyw thoughts? It seems sort of odd to show examples with Future<void> main() everywhere, but it also seems odd to do it in some places but not others.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about the rationale of changing from converts createOrderMessage () to concerts main().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to remember why we changed from createOrderMessage() to main(). I agree that it might be better to show something that isn't main(), so we don't have to dance around the return type declaration being OK to leave off. (Or we can do the dance later.)

I think the text was inconsistent about Future<void> vs. Future<TypeOfWhatYouReturn>, so we picked Future<void>. Also, maybe we wanted to handle the no-return case? But we could do that separately.

Copy link
Contributor

@galeyang galeyang Aug 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, maybe we wanted to handle the no-return case? But we could do that separately.

It seems that both examples in the What is a future? section has handled the no-return case using Future<void>.

{% endprettify %}

Now that you have an async function, you can use the `await` keyword to wait for a future to complete:
Now that you have an async function, you can use the `await` keyword to wait
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async function -> async function (global)

for a future to complete:
{% prettify dart %}
print(await createOrderMessage());
{% endprettify %}
Expand Down Expand Up @@ -263,15 +268,19 @@ main() [!async!] {
</div>

The asynchronous example is different in three ways:
* The return type for `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()`.
* The return type for `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()`.

{{ site.alert.secondary }}
**Key terms:**
* **async**: You can use the `async` keyword before a function's body to mark it as
asynchronous.
* **async function**: An async function is a function labeled with the `async` keyword.
* **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 an
asynchronous expression. The `await` keyword only works within an async function.
{{ site.alert.end }}
Expand Down Expand Up @@ -335,16 +344,20 @@ https://api.dartlang.org/stable/2.4.0/dart-developer/dart-developer-library.html
#### Part 1: `reportUserRole()`

Add code to the `reportUserRole` function so that it does the following:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reportUserRole -> reportUserRole()

<!-- Some bulleted items are intentionally lacking punctuation to avoid confusing the users about characters in string values -->
* `reportUserRole()` Returns a future that completes with the following string: `"User role: <user role>"`
* Note: You must use the actual value returned by `getRole()`; copying and pasting the example return value won't make the test pass.
<!-- Some bulleted items are intentionally lacking punctuation to avoid
confusing the users about characters in string values -->
* `reportUserRole()` Returns a future that completes with the following
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete reportUserRole()

string: `"User role: <user role>"`
* Note: You must use the actual value returned by `getRole()`; copying and
pasting 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()`:
* `reportLogins()` returns the string `"Total number of logins: <# of logins>"`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar changes to above

* Note: You must use the actual value returned by `getLoginAmount()`; copying and pasting the example return value won't make the test pass.
* Note: You must use the actual value returned by `getLoginAmount()`; copying
and 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()`.

Expand Down Expand Up @@ -399,9 +412,11 @@ operations, your code will call the following function, which is provided for yo
{:.table .table-striped}

Use `async` and `await` to do the following:
* Implement an asynchronous `changeUsername()` function that calls the provided asynchronous function `getNewUsername()` and returns its result.
* Implement an asynchronous `changeUsername()` function that calls the provided
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flow seems slightly off. How about this:

Use async and await to implement an asynchronous changeUsername() function that does the following:

  • Calls the provided asynchronous function getNewUsername() and returns the result.
    • Example...
  • Catches any error that occurs and returns the string value of the error.
    • You can use...

asynchronous function `getNewUsername()` and returns its result.
* Example return value from `changeUsername()`: `"jane_smith_92"`
* Because `getNewUsername()` can encounter errors, the `changeUsername()` function must __catch and return any errors__ and it must __stringify the error before returning it__.
* Because `getNewUsername()` can encounter errors, the `changeUsername()`
function must __catch and return any errors__ and it must __stringify the error before returning it__.
* You can use the [toString()]({{site.dart_api}}/stable/dart-core/ArgumentError/toString.html) method to stringify both [Exceptions]({{site.dart_api}}/stable/dart-core/Exception-class.html) and [Errors.]({{site.dart_api}}/stable/dart-core/Error-class.html)

<!-- [//]: https://gist.github.com/858f71f0ad0e70051999bcafa41806a3 -->
Expand All @@ -417,11 +432,12 @@ width="100%" >

Copy link
Contributor

@galeyang galeyang Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(my quote is weird --- I was referring to the heading of "Exercise: Putting it all together)
In ToC, it's under "Handling errors". I guess it should be move a level up? Btw, I like that latest ToC is more concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kwalrath I could be wrong, but I think ToC level for exercises and examples may be a point of disagreement here. If it is, I'll leave it to you two.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm out next Monday. Feel free to ignore this one if other content are ready to go. It's a minor point. We can revisit later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this exercise doesn't belong under Handling errors. Changing ### to ## makes sense here.

It's time to practice what you've learned in one final exercise.
To simulate asynchronous operations, this exercise provides the async functions
`getUsername()` and `logoutUser()`. You don’t need to implement these provided functions.
`getUsername()` and `logoutUser()`. You don’t need to implement these provided
functions.
You don't need to implement `main()`.

To simulate asynchronous operations, your code will call the following functions, which are
provided for you:
To simulate asynchronous operations, your code will call the following
functions, which are provided for you:

|------------------+-----------------------------------+-------------|
| Function | Type signature | Description |
Expand All @@ -439,9 +455,12 @@ Write the following:

#### Part 2: `greetUser()`
* Write a function `greetUser()` that takes no arguments.
* To obtain the username, `greetUser()` calls the provided asynchronous function `getUsername()`
* `greetUser()` creates a greeting for the user by calling `addHello()`, passing it the username, and returning the result.
* For example, if the username is "Jenny", `greetUser()` should create and return the greeting "Hello Jenny"
* To obtain the username, `greetUser()` calls the provided asynchronous
function `getUsername()`
* `greetUser()` creates a greeting for the user by calling `addHello()`,
passing it the username, and returning the result.
* For example, if the username is "Jenny", `greetUser()` should create and
return the greeting "Hello Jenny"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jenny" -> Jenny".
OR
the greeting... -> the following: "Hello Jenny"


#### Part 3: `sayGoodbye()`
<!-- pull out descriptions of -->
Expand All @@ -450,7 +469,8 @@ Write the following:
* `sayGoodbye()` catches any errors.
* `sayGoodbye()` calls the provided asynchronous function `logoutUser()`.
<!-- italicize <result> -->
* If `logoutUser()` succeeds, `sayGoodbye()` returns the string "\<result\> Thanks, see you next time" where \<result\> is the String value returned by calling `logoutUser()`.
* If `logoutUser()` succeeds, `sayGoodbye()` returns the string "\<result\>
Thanks, see you next time" where \<result\> is the String value returned by calling `logoutUser()`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returned by calling -> produced (eventually) by logoutUser().

(or something else that acknowledges that logoutUser doesn't return a string value)


<!-- [//]: https://gist.github.com/f601d25bc2833c957186e3c6bf71effc -->
<iframe
Expand Down