| OLD | NEW |
| 1 --- | 1 --- |
| 2 layout: default | 2 layout: default |
| 3 title: "The Event Loop and Dart" | 3 title: "The Event Loop and Dart" |
| 4 description: "Learn how Dart handles the event queue and microtask queue, so you
can write better asynchronous code with fewer surprises." | 4 description: "Learn how Dart handles the event queue and microtask queue, so you
can write better asynchronous code with fewer surprises." |
| 5 rel: | 5 rel: |
| 6 author: kathy-walrath | 6 author: kathy-walrath |
| 7 has-permalinks: true | 7 has-permalinks: true |
| 8 article: | 8 article: |
| 9 written_on: 2013-09-30 | 9 written_on: 2013-09-30 |
| 10 updated_on: 2013-10-22 |
| 10 collection: performance | 11 collection: performance |
| 11 --- | 12 --- |
| 12 | 13 |
| 13 # {{ page.title }} | 14 # {{ page.title }} |
| 14 | 15 |
| 15 _Written by Kathy Walrath | 16 _Written by Kathy Walrath |
| 16 <br> | 17 <br> |
| 17 September 2013_ | 18 September 2013 (updated October 2013)_ |
| 18 | 19 |
| 19 Asynchronous code is everywhere in Dart. | 20 Asynchronous code is everywhere in Dart. |
| 20 Many library functions return Future objects, | 21 Many library functions return Future objects, |
| 21 and you can register handlers to respond to events such as | 22 and you can register handlers to respond to events such as |
| 22 mouse clicks, file I/O completions, and timer expirations. | 23 mouse clicks, file I/O completions, and timer expirations. |
| 23 | 24 |
| 24 This article describes Dart’s event loop architecture, | 25 This article describes Dart’s event loop architecture, |
| 25 so that you can write better asynchronous code with fewer surprises. | 26 so that you can write better asynchronous code with fewer surprises. |
| 26 You’ll learn options for scheduling future tasks, | 27 You’ll learn options for scheduling future tasks, |
| 27 and you’ll be able to predict the order of execution. | 28 and you’ll be able to predict the order of execution. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 Once both queues are empty and no more events are expected, | 139 Once both queues are empty and no more events are expected, |
| 139 the app’s _embedder_ (such as the browser or a test framework) | 140 the app’s _embedder_ (such as the browser or a test framework) |
| 140 can dispose of the app. | 141 can dispose of the app. |
| 141 | 142 |
| 142 <aside class="alert alert-info" markdown="1"> | 143 <aside class="alert alert-info" markdown="1"> |
| 143 **Note:** | 144 **Note:** |
| 144 If a web app’s user closes its window, | 145 If a web app’s user closes its window, |
| 145 then the web app might exit before its event queue is empty. | 146 then the web app might exit before its event queue is empty. |
| 146 </aside> | 147 </aside> |
| 147 | 148 |
| 148  | 149  |
| 149 | 150 |
| 150 <aside class="alert alert-warning" markdown="1"> | 151 <aside class="alert alert-warning" markdown="1"> |
| 151 **Important:** | 152 **Important:** |
| 152 While the event loop is executing tasks from the microtask queue, | 153 While the event loop is executing tasks from the microtask queue, |
| 153 the event queue is stuck: | 154 the event queue is stuck: |
| 154 the app can’t | 155 the app can’t |
| 155 draw graphics, handle mouse clicks, react to I/O, and so on. | 156 draw graphics, handle mouse clicks, react to I/O, and so on. |
| 156 </aside> | 157 </aside> |
| 157 | 158 |
| 158 Although you can predict the _order_ of task execution, | 159 Although you can predict the _order_ of task execution, |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 process other events from the event queue. | 207 process other events from the event queue. |
| 207 The next section gives details on scheduling code to run later. | 208 The next section gives details on scheduling code to run later. |
| 208 | 209 |
| 209 ## How to schedule a task | 210 ## How to schedule a task |
| 210 | 211 |
| 211 When you need to specify some code to be executed later, | 212 When you need to specify some code to be executed later, |
| 212 you can use the following APIs provided by the dart:async library: | 213 you can use the following APIs provided by the dart:async library: |
| 213 | 214 |
| 214 1. The **Future** class, | 215 1. The **Future** class, |
| 215 which adds an item to the end of the **event queue**. | 216 which adds an item to the end of the **event queue**. |
| 216 1. The top-level **runAsync()** function, | 217 1. The top-level **scheduleMicrotask()** function, |
| 217 which adds an item to the end of the **microtask queue**. | 218 which adds an item to the end of the **microtask queue**. |
| 218 | 219 |
| 220 <aside class="alert alert-info" markdown="1"> |
| 221 **Note:** |
| 222 The **scheduleMicrotask()** function used to be named **runAsync()**. |
| 223 (See the [announcement](https://groups.google.com/a/dartlang.org/forum/#!msg/mis
c/7sAIhWXfIKQ/PzYJy1QqtWUJ).) |
| 224 </aside> |
| 225 |
| 219 Examples of using these APIs are in the next section under | 226 Examples of using these APIs are in the next section under |
| 220 [Event queue: new Future()](#event-queue-new-future) and | 227 [Event queue: new Future()](#event-queue-new-future) and |
| 221 [Microtask queue: runAsync()](#microtask-queue-runasync). | 228 [Microtask queue: scheduleMicrotask()](#microtask-queue). |
| 222 | 229 |
| 223 ### Use the appropriate queue (usually: the event queue) | 230 ### Use the appropriate queue (usually: the event queue) |
| 224 | 231 |
| 225 Whenever possible, schedule tasks on the event queue, with Future. | 232 Whenever possible, schedule tasks on the event queue, with Future. |
| 226 Using the event queue helps keep the the microtask queue short, | 233 Using the event queue helps keep the the microtask queue short, |
| 227 reducing the likelihood of the microtask queue starving the event queue. | 234 reducing the likelihood of the microtask queue starving the event queue. |
| 228 | 235 |
| 229 If a task absolutely must complete before | 236 If a task absolutely must complete before |
| 230 any items from the event queue are handled, | 237 any items from the event queue are handled, |
| 231 then you should usually just execute the function immediately. | 238 then you should usually just execute the function immediately. |
| 232 If you can’t, then use runAsync() to add an item to the microtask queue. | 239 If you can’t, then use scheduleMicrotask() to |
| 240 add an item to the microtask queue. |
| 233 For example, in a web app use a microtask to | 241 For example, in a web app use a microtask to |
| 234 avoid prematurely releasing a js-interop proxy or | 242 avoid prematurely releasing a js-interop proxy or |
| 235 ending an IndexedDB transaction or event handler. | 243 ending an IndexedDB transaction or event handler. |
| 236 | 244 |
| 237  | 245  |
| 238 | 246 |
| 239 | 247 |
| 240 #### Event queue: new Future() | 248 #### Event queue: new Future() |
| 241 | 249 |
| 242 To schedule a task on the event queue, | 250 To schedule a task on the event queue, |
| 243 use `new Future()` or `new Future.delayed()`. | 251 use `new Future()` or `new Future.delayed()`. |
| 244 These are two of the | 252 These are two of the |
| 245 [Future](http://api.dartlang.org/dart_async/Future.html) | 253 [Future](http://api.dartlang.org/dart_async/Future.html) |
| 246 constructors defined in the dart:async library. | 254 constructors defined in the dart:async library. |
| 247 | 255 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 and _that_ task executes the function passed into then(). | 323 and _that_ task executes the function passed into then(). |
| 316 1. The **Future()** and **Future.delayed()** constructors | 324 1. The **Future()** and **Future.delayed()** constructors |
| 317 don’t complete immediately; | 325 don’t complete immediately; |
| 318 they add an item to the event queue. | 326 they add an item to the event queue. |
| 319 1. The **Future.value()** constructor completes in a microtask, | 327 1. The **Future.value()** constructor completes in a microtask, |
| 320 similar to #2. | 328 similar to #2. |
| 321 1. The **Future.sync()** constructor executes its function argument immediately | 329 1. The **Future.sync()** constructor executes its function argument immediately |
| 322 and (unless that function returns a Future) | 330 and (unless that function returns a Future) |
| 323 completes in a microtask, similar to #2. | 331 completes in a microtask, similar to #2. |
| 324 | 332 |
| 325 #### Microtask queue: runAsync() | 333 #### Microtask queue: scheduleMicrotask() |
| 326 | 334 |
| 327 The dart:async library defines runAsync() as a top-level function. | 335 The dart:async library defines scheduleMicrotask() as a top-level function. |
| 328 You can call runAsync() like this: | 336 You can call scheduleMicrotask() like this: |
| 329 | 337 |
| 330 {% prettify dart %} | 338 {% prettify dart %} |
| 331 runAsync(() { | 339 scheduleMicrotask(() { |
| 332 // ...code goes here... | 340 // ...code goes here... |
| 333 }); | 341 }); |
| 334 {% endprettify %} | 342 {% endprettify %} |
| 335 | 343 |
| 336 Due to bugs [9001](https://code.google.com/p/dart/issues/detail?id=9001) | 344 Due to bugs [9001](https://code.google.com/p/dart/issues/detail?id=9001) |
| 337 and [9002](https://code.google.com/p/dart/issues/detail?id=9002), | 345 and [9002](https://code.google.com/p/dart/issues/detail?id=9002), |
| 338 the first call to runAsync() schedules a task on the event queue; | 346 the first call to scheduleMicrotask() schedules a task on the event queue; |
| 339 this task creates the microtask queue and | 347 this task creates the microtask queue and |
| 340 enqueues the function specified to runAsync(). | 348 enqueues the function specified to scheduleMicrotask(). |
| 341 As long as the microtask queue has at least one entry, | 349 As long as the microtask queue has at least one entry, |
| 342 subsequent calls to runAsync() correctly add to the microtask queue. | 350 subsequent calls to scheduleMicrotask() correctly add to the microtask queue. |
| 343 Once the microtask queue is empty, | 351 Once the microtask queue is empty, |
| 344 it must be created again the next time runAsync() is called. | 352 it must be created again the next time scheduleMicrotask() is called. |
| 345 | 353 |
| 346 The upshot of these bugs: | 354 The upshot of these bugs: |
| 347 The first task that you schedule with runAsync() seems | 355 The first task that you schedule with scheduleMicrotask() seems |
| 348 like it’s on the event queue. | 356 like it’s on the event queue. |
| 349 | 357 |
| 350 A workaround is to put your first call to runAsync() before | 358 A workaround is to put your first call to scheduleMicrotask() before |
| 351 your first call to new Future(). | 359 your first call to new Future(). |
| 352 This creates the microtask queue before | 360 This creates the microtask queue before |
| 353 executing other tasks on the event queue. | 361 executing other tasks on the event queue. |
| 354 However, it doesn’t stop external events from being added to the event queue. | 362 However, it doesn’t stop external events from being added to the event queue. |
| 355 It also doesn’t help when you have a delayed task. | 363 It also doesn’t help when you have a delayed task. |
| 356 | 364 |
| 357 Another way to add a task to the microtask queue is | 365 Another way to add a task to the microtask queue is |
| 358 to invoke then() on a Future that’s already complete. | 366 to invoke then() on a Future that’s already complete. |
| 359 See the previous section for more information. | 367 See the previous section for more information. |
| 360 | 368 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 Still, won’t you feel smart if you can answer these questions correctly? | 406 Still, won’t you feel smart if you can answer these questions correctly? |
| 399 | 407 |
| 400 ### Question #1 | 408 ### Question #1 |
| 401 | 409 |
| 402 What does this sample print out? | 410 What does this sample print out? |
| 403 | 411 |
| 404 {% prettify dart %} | 412 {% prettify dart %} |
| 405 import 'dart:async'; | 413 import 'dart:async'; |
| 406 main() { | 414 main() { |
| 407 print('main #1 of 2'); | 415 print('main #1 of 2'); |
| 408 runAsync(() => print('runAsync #1 of 2')); | 416 scheduleMicrotask(() => print('microtask #1 of 2')); |
| 409 | 417 |
| 410 new Future.delayed(new Duration(seconds:1), | 418 new Future.delayed(new Duration(seconds:1), |
| 411 () => print('future #1 (delayed)')); | 419 () => print('future #1 (delayed)')); |
| 412 new Future(() => print('future #2 of 3')); | 420 new Future(() => print('future #2 of 3')); |
| 413 new Future(() => print('future #3 of 3')); | 421 new Future(() => print('future #3 of 3')); |
| 414 | 422 |
| 415 runAsync(() => print('runAsync #2 of 2')); | 423 scheduleMicrotask(() => print('microtask #2 of 2')); |
| 416 | 424 |
| 417 print('main #2 of 2'); | 425 print('main #2 of 2'); |
| 418 } | 426 } |
| 419 {% endprettify %} | 427 {% endprettify %} |
| 420 | 428 |
| 421 The answer: | 429 The answer: |
| 422 | 430 |
| 423 <pre> | 431 <pre> |
| 424 main #1 of 2 | 432 main #1 of 2 |
| 425 main #2 of 2 | 433 main #2 of 2 |
| 426 runAsync #1 of 2 | 434 microtask #1 of 2 |
| 427 runAsync #2 of 2 | 435 microtask #2 of 2 |
| 428 future #2 of 3 | 436 future #2 of 3 |
| 429 future #3 of 3 | 437 future #3 of 3 |
| 430 future #1 (delayed) | 438 future #1 (delayed) |
| 431 </pre> | 439 </pre> |
| 432 | 440 |
| 433 That order should be what you expected, | 441 That order should be what you expected, |
| 434 since the example’s code executes in three batches: | 442 since the example’s code executes in three batches: |
| 435 | 443 |
| 436 1. code in the main() function | 444 1. code in the main() function |
| 437 1. tasks in the microtask queue (runAsync()) | 445 1. tasks in the microtask queue (scheduleMicrotask()) |
| 438 1. tasks in the event queue (new Future() or new Future.delayed()) | 446 1. tasks in the event queue (new Future() or new Future.delayed()) |
| 439 | 447 |
| 440 Keep in mind that all the calls in the main() function execute synchronously, | 448 Keep in mind that all the calls in the main() function execute synchronously, |
| 441 start to finish. | 449 start to finish. |
| 442 First main() calls print(), then runAsync(), | 450 First main() calls print(), then scheduleMicrotask(), |
| 443 then new Future.delayed(), then new Future(), and so on. | 451 then new Future.delayed(), then new Future(), and so on. |
| 444 Only the callbacks—the code in the closure bodies specified as | 452 Only the callbacks—the code in the closure bodies specified as |
| 445 arguments to runAsync(), new Future.delayed(), and | 453 arguments to scheduleMicrotask(), new Future.delayed(), and |
| 446 new Future()—execute at a later time. | 454 new Future()—execute at a later time. |
| 447 | 455 |
| 448 <aside class="alert alert-info" markdown="1"> | 456 <aside class="alert alert-info" markdown="1"> |
| 449 **Note:** | 457 **Note:** |
| 450 Currently, if you comment out the first call to runAsync, | 458 Currently, if you comment out the first call to scheduleMicrotask(), |
| 451 then the callbacks for futures #2 and #3 execute before runAsync #2. | 459 then the callbacks for futures #2 and #3 execute before microtask #2. |
| 452 This is due to bugs 9001 and 9002, as discussed in | 460 This is due to bugs 9001 and 9002, as discussed in |
| 453 [Microtask queue: runAsync()](#microtask-queue-runasync). | 461 [Microtask queue: scheduleMicrotask()](#microtask-queue). |
| 454 </aside> | 462 </aside> |
| 455 | 463 |
| 456 ### Question #2 | 464 ### Question #2 |
| 457 | 465 |
| 458 Here’s a more complex example. | 466 Here’s a more complex example. |
| 459 If you can correctly predict the output of this code, | 467 If you can correctly predict the output of this code, |
| 460 you get a gold star. | 468 you get a gold star. |
| 461 | 469 |
| 462 {% prettify dart %} | 470 {% prettify dart %} |
| 463 import 'dart:async'; | 471 import 'dart:async'; |
| 464 main() { | 472 main() { |
| 465 print('main #1 of 2'); | 473 print('main #1 of 2'); |
| 466 runAsync(() => print('runAsync #1 of 3')); | 474 scheduleMicrotask(() => print('microtask #1 of 3')); |
| 467 | 475 |
| 468 new Future.delayed(new Duration(seconds:1), | 476 new Future.delayed(new Duration(seconds:1), |
| 469 () => print('future #1 (delayed)')); | 477 () => print('future #1 (delayed)')); |
| 470 | 478 |
| 471 new Future(() => print('future #2 of 4')) | 479 new Future(() => print('future #2 of 4')) |
| 472 .then((_) => print('future #2a')) | 480 .then((_) => print('future #2a')) |
| 473 .then((_) { | 481 .then((_) { |
| 474 print('future #2b'); | 482 print('future #2b'); |
| 475 runAsync(() => print('runAsync #0 (from future #2b)')); | 483 scheduleMicrotask(() => print('microtask #0 (from future #2b)')); |
| 476 }) | 484 }) |
| 477 .then((_) => print('future #2c')); | 485 .then((_) => print('future #2c')); |
| 478 | 486 |
| 479 runAsync(() => print('runAsync #2 of 3')); | 487 scheduleMicrotask(() => print('microtask #2 of 3')); |
| 480 | 488 |
| 481 new Future(() => print('future #3 of 4')) | 489 new Future(() => print('future #3 of 4')) |
| 482 .then((_) => new Future( | 490 .then((_) => new Future( |
| 483 () => print('future #3a (a new future)'))) | 491 () => print('future #3a (a new future)'))) |
| 484 .then((_) => print('future #3b')); | 492 .then((_) => print('future #3b')); |
| 485 | 493 |
| 486 new Future(() => print('future #4 of 4')); | 494 new Future(() => print('future #4 of 4')); |
| 487 runAsync(() => print('runAsync #3 of 3')); | 495 scheduleMicrotask(() => print('microtask #3 of 3')); |
| 488 print('main #2 of 2'); | 496 print('main #2 of 2'); |
| 489 } | 497 } |
| 490 {% endprettify %} | 498 {% endprettify %} |
| 491 | 499 |
| 492 The output, assuming bugs 9001/9002 aren't fixed: | 500 The output, assuming bugs 9001/9002 aren't fixed: |
| 493 | 501 |
| 494 <pre> | 502 <pre> |
| 495 main #1 of 2 | 503 main #1 of 2 |
| 496 main #2 of 2 | 504 main #2 of 2 |
| 497 runAsync #1 of 3 | 505 microtask #1 of 3 |
| 498 runAsync #2 of 3 | 506 microtask #2 of 3 |
| 499 runAsync #3 of 3 | 507 microtask #3 of 3 |
| 500 future #2 of 4 | 508 future #2 of 4 |
| 501 future #2a | 509 future #2a |
| 502 future #2b | 510 future #2b |
| 503 future #2c | 511 future #2c |
| 504 future #3 of 4 | 512 future #3 of 4 |
| 505 future #4 of 4 | 513 future #4 of 4 |
| 506 runAsync #0 (from future #2b) | 514 microtask #0 (from future #2b) |
| 507 future #3a (a new future) | 515 future #3a (a new future) |
| 508 future #3b | 516 future #3b |
| 509 future #1 (delayed) | 517 future #1 (delayed) |
| 510 </pre> | 518 </pre> |
| 511 | 519 |
| 512 <aside class="alert alert-info" markdown="1"> | 520 <aside class="alert alert-info" markdown="1"> |
| 513 **Note:** | 521 **Note:** |
| 514 Due to bugs 9001/9002, | 522 Due to bugs 9001/9002, |
| 515 runAsync #0 executes after future #4; | 523 microtask #0 executes after future #4; |
| 516 it should instead execute before future #3. | 524 it should instead execute before future #3. |
| 517 This bug shows up because by the time future #2b executes, | 525 This bug shows up because by the time future #2b executes, |
| 518 no runAsync tasks are queued, | 526 no microtasks are queued, |
| 519 so runAsync #0 results in a new task on the event queue, | 527 so microtask #0 results in a new task on the event queue, |
| 520 which creates a new microtask queue. | 528 which creates a new microtask queue. |
| 521 This microtask queue has a task for runAsync #0. | 529 This microtask queue contains microtask #0. |
| 522 If you comment out runAsync #1, | 530 If you comment out microtask #1, |
| 523 then the runAsync tasks all appear together just after future #2c, | 531 then the microtasks all appear together just after future #2c, |
| 524 and before future #3. | 532 and before future #3. |
| 525 </aside> | 533 </aside> |
| 526 | 534 |
| 527 Like before, the main() function executes, | 535 Like before, the main() function executes, |
| 528 and then the tasks on the microtask queue, | 536 and then everything on the microtask queue, |
| 529 and then those on the event queue. | 537 and then tasks on the event queue. |
| 530 Here are a few interesting points: | 538 Here are a few interesting points: |
| 531 | 539 |
| 532 * When the then() callback for future 3 calls new Future(), | 540 * When the then() callback for future 3 calls new Future(), |
| 533 it creates a new task (#3a) that’s added to the end of the event queue. | 541 it creates a new task (#3a) that’s added to the end of the event queue. |
| 534 * All the then() callbacks execute as soon as | 542 * All the then() callbacks execute as soon as |
| 535 the Future they’re invoked on completes. | 543 the Future they’re invoked on completes. |
| 536 Thus, future 2, 2a, 2b, and 2c execute all in one go, | 544 Thus, future 2, 2a, 2b, and 2c execute all in one go, |
| 537 before control returns to the embedder. | 545 before control returns to the embedder. |
| 538 Similarly, future 3a and 3b execute all in one go. | 546 Similarly, future 3a and 3b execute all in one go. |
| 539 * If you change the 3a code from | 547 * If you change the 3a code from |
| 540 `then((_) => new Future(...))` to | 548 `then((_) => new Future(...))` to |
| 541 `then((_) {new Future(...); })`, | 549 `then((_) {new Future(...); })`, |
| 542 then "future #3b" appears earlier | 550 then "future #3b" appears earlier |
| 543 (after future #3, instead of future #3a). | 551 (after future #3, instead of future #3a). |
| 544 The reason is that returning a Future from your callback | 552 The reason is that returning a Future from your callback |
| 545 is how you get then() (which itself returns a new Future) | 553 is how you get then() (which itself returns a new Future) |
| 546 to _chain_ those two Futures together, | 554 to _chain_ those two Futures together, |
| 547 so that the Future returned by then() completes | 555 so that the Future returned by then() completes |
| 548 when the Future returned by the callback completes. | 556 when the Future returned by the callback completes. |
| 549 See the [then() reference](http://api.dartlang.org/docs/releases/latest/dart_asy
nc/Future.html#then) | 557 See the [then() reference](http://api.dartlang.org/docs/releases/latest/dart_asy
nc/Future.html#then) |
| 550 for more information. | 558 for more information. |
| 551 | 559 |
| 552 | 560 |
| 553 #### Annotated sample and output | 561 #### Annotated sample and output |
| 554 | 562 |
| 555 Here are some figures that might clarify the answer to question #2. | 563 Here are some figures that might clarify the answer to question #2. |
| 556 First, here’s the annotated program source: | 564 First, here’s the annotated program source: |
| 557 | 565 |
| 558  | 566  |
| 559 | 567 |
| 560 And here’s what the queues and output look like at various points in time, | 568 And here’s what the queues and output look like at various points in time, |
| 561 assuming no external events come in: | 569 assuming no external events come in: |
| 562 | 570 |
| 563  | 571  |
| 564 | 572 |
| 565 | 573 |
| 566 ## Summary | 574 ## Summary |
| 567 | 575 |
| 568 You should now understand Dart’s event loops and how to schedule tasks. | 576 You should now understand Dart’s event loops and how to schedule tasks. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 593 avoid compute-intensive tasks on either event loop. | 601 avoid compute-intensive tasks on either event loop. |
| 594 * To perform compute-intensive tasks, | 602 * To perform compute-intensive tasks, |
| 595 create additional isolates or workers. | 603 create additional isolates or workers. |
| 596 | 604 |
| 597 As you write asynchronous code, you might find these resources helpful: | 605 As you write asynchronous code, you might find these resources helpful: |
| 598 | 606 |
| 599 * [Using Future Based APIs](/articles/using-future-based-apis/) | 607 * [Using Future Based APIs](/articles/using-future-based-apis/) |
| 600 * [Futures and Error Handling](/articles/futures-and-error-handling/) | 608 * [Futures and Error Handling](/articles/futures-and-error-handling/) |
| 601 * [dart:async - Asynchronous Programming](/docs/dart-up-and-running/contents/ch0
3.html#ch03-asynchronous-programming) section of the library tour | 609 * [dart:async - Asynchronous Programming](/docs/dart-up-and-running/contents/ch0
3.html#ch03-asynchronous-programming) section of the library tour |
| 602 * [dart:async API reference](http://api.dartlang.org/dart_async.html) | 610 * [dart:async API reference](http://api.dartlang.org/dart_async.html) |
| OLD | NEW |