OLD | NEW |
| (Empty) |
1 --- | |
2 layout: article | |
3 title: "Using Future Based APIs" | |
4 description: "A first look at Futures and how to use them to make your | |
5 asynchronous code better." | |
6 rel: | |
7 author: shailen-tuli | |
8 has-permalinks: true | |
9 article: | |
10 written_on: 2013-02-07 | |
11 collection: libraries-and-apis | |
12 --- | |
13 | |
14 {% include toc.html %} | |
15 | |
16 # {{ page.title }} | |
17 | |
18 _Written by Shailen Tuli, February 2013_ | |
19 | |
20 ## Introduction | |
21 | |
22 Dart is a single-threaded programming language. If any code blocks the thread | |
23 of execution, the program effectively freezes. Let's look at some code where | |
24 this happens: | |
25 | |
26 {% prettify dart %} | |
27 import 'dart:io'; | |
28 | |
29 void printDailyNewsDigest() { | |
30 File file = new File("dailyNewsDigest.txt"); | |
31 print(file.readAsStringSync()); | |
32 } | |
33 | |
34 void main() { | |
35 printDailyNewsDigest(); | |
36 printWinningLotteryNumbers(); | |
37 printWeatherForecast(); | |
38 printBaseballScore(); | |
39 } | |
40 {% endprettify %} | |
41 | |
42 Our program reads the news of the day from a file, `dailyNewsDigest.txt`, | |
43 prints it, and then prints a bunch of other items of interest to the user: | |
44 | |
45 <Contents of dailyNewsDigest.txt> | |
46 Winning lotto numbers: [23, 63, 87, 26, 2] | |
47 Tomorrow's forecast: 70F, sunny. | |
48 Baseball score: Red Sox 10, Yankees 0 | |
49 | |
50 Our code is problematic: since `readAsStringSync()` blocks, the remaining code | |
51 runs only when `readAsStringSync()` returns with the contents of the file, | |
52 _however long that takes_. And if reading the file takes a long time, the | |
53 user waits passively, wondering if she won the lottery, what tomorrow's weather | |
54 will be like, and who won today's game. Not good. | |
55 | |
56 To help keep the application responsive, Dart library authors use an | |
57 asynchronous model when defining functions that do potentially expensive work. | |
58 Such functions return their value using a Future. | |
59 | |
60 ## What is a Future? | |
61 | |
62 A Future represents a means for getting a value sometime in the future. When a | |
63 function that returns a Future is invoked, two things happen: | |
64 | |
65 1. The function queues up work to be done and returns an uncompleted Future | |
66 object immediately. | |
67 1. Later, when a value is available, the Future object completes with that | |
68 value (or with an error; we'll discuss that later). | |
69 | |
70 To get the value that the Future represents, use the `then()` method to | |
71 register a callback. This callback fires when the Future completes. | |
72 | |
73 ## Using a Future | |
74 | |
75 Let's rewrite `printDailyNewsDigest()` to get the file contents | |
76 asynchronously: | |
77 | |
78 {% prettify dart %} | |
79 import 'dart:io'; | |
80 import 'dart:async'; | |
81 | |
82 void printDailyNewsDigest() { | |
83 File file = new File("dailyNewsDigest.txt"); | |
84 Future future = file.readAsString(); | |
85 future.then((content) { | |
86 print(content); | |
87 }); | |
88 } | |
89 {% endprettify %} | |
90 | |
91 The `printDailyNewsDigest()` function now uses `readAsString()`, which is | |
92 non-blocking. Calling `readAsString()` queues up the work to be done but | |
93 doesn't stop the rest of the code from executing. The program prints the | |
94 lottery numbers, the forecast, and the baseball score; when | |
95 `readAsString()` finishes reading the news file, the program prints its | |
96 contents. If `readAsString()` takes a little while to complete its work, no | |
97 great harm is done: the user gets to read other things before the daily news | |
98 digest is printed. | |
99 | |
100 Winning lotto numbers: [23, 63, 87, 26, 2] | |
101 Tomorrow's forecast: 70F, sunny. | |
102 Baseball score: Red Sox 10, Yankees 0 | |
103 <Contents of dailyNewsDigest.txt> | |
104 | |
105 ## Sequence of events during code execution | |
106 | |
107 The preceding code executes in three steps: | |
108 | |
109 1. The program enters `main()`, which calls `printDailyNewsDigest()`, which | |
110 queues up the file reading task. After calling the remaining print functions, | |
111 `main()` exits, but the program continues. | |
112 1. The work scheduled by `readAsString()` is performed, and the contents of the | |
113 file are read into memory. When the entire file is read, the Future completes | |
114 with the file contents. | |
115 1. The callback registered within `then()` fires and prints the contents | |
116 of the news file. | |
117 | |
118 Calling `then()` returns a new Future, which completes with the value | |
119 returned by `then()`'s callback. This means that calls to `then()` can be | |
120 chained (we'll see examples of this later). | |
121 | |
122 ## Handling errors when dealing with Futures | |
123 | |
124 If a Future-returning function completes with an error, the Future returned by | |
125 `then()` also completes with an error. We can capture that error using | |
126 `catchError()`: | |
127 | |
128 {% prettify dart %} | |
129 | |
130 void printDailyNewsDigest() { | |
131 File file = new File("dailyNewsDigest.txt"); | |
132 Future future = file.readAsString(); | |
133 future.then((content) => doSomethingWith(content)) | |
134 .catchError((e) => handleError(e)); | |
135 } | |
136 {% endprettify %} | |
137 | |
138 If `dailyNewsDigest.txt` doesn't exist or isn't available for reading, | |
139 the code above executes as follows: | |
140 | |
141 1. `readAsString()`'s Future completes with an error. | |
142 1. `then()`'s Future completes with an error. | |
143 1. `catchError()`'s callback handles the error, `catchError()`'s Future | |
144 completes normally, and the error does not propagate. | |
145 | |
146 <aside class="alert alert-info" markdown="1"> | |
147 Chaining catchError() to then() is a common pattern when working with | |
148 functions that return Futures. | |
149 <strong> | |
150 Consider this pairing the asynchronous equivalent of try-catch blocks. | |
151 </strong> | |
152 </aside> | |
153 | |
154 Like `then()`, `catchError()` returns a new Future that completes with | |
155 the return value of its callback. | |
156 | |
157 For more details and examples, read | |
158 [Futures and Error Handling](/articles/futures-and-error-handling/). | |
159 | |
160 | |
161 ## Calling multiple functions that return Futures | |
162 | |
163 Consider three functions, `expensiveA()`, `expensiveB()`, and `expensiveC()`, | |
164 that return Futures. You can invoke them sequentially (one function starts | |
165 when a previous one completes), or you can kick off all of them at the same | |
166 time and do something once all the values return. The Future interface | |
167 is fluid enough to deal with both use cases. | |
168 | |
169 ### Chaining function calls using then() | |
170 | |
171 When Future-returning functions need to run in order, use | |
172 chained `then()` calls: | |
173 | |
174 {% prettify dart %} | |
175 expensiveA().then((aValue) => expensiveB()) | |
176 .then((bValue) => expensiveC()) | |
177 .then((cValue) => doSomethingWith(cValue)); | |
178 {% endprettify %} | |
179 | |
180 Nested callbacks also work, but they're harder to read and not as Dart-y. | |
181 | |
182 ### Waiting on multiple Futures to complete using Future.wait() | |
183 | |
184 If the order of execution of the functions is not important, | |
185 you can use `Future.wait()` to handle multiple Future objects | |
186 without having to explicitly chain function calls. | |
187 | |
188 The functions get triggered in quick succession; when all of them | |
189 complete with a value, `Future.wait()` returns a new Future. | |
190 This Future completes with a list containing the values produced by | |
191 each function. | |
192 | |
193 {% prettify dart %} | |
194 Future.wait([expensiveA(), expensiveB(), expensiveC()]) | |
195 .then((List responses) => chooseBestResponse(responses)) | |
196 .catchError((e) => handleError(e)); | |
197 {% endprettify %} | |
198 | |
199 If any of the invoked functions completes with an error, the Future returned | |
200 by `Future.wait()` also completes with an error. Use `catchError()` to handle | |
201 the error. | |
202 | |
203 ## More information | |
204 | |
205 Read the following documentation for more details on using Futures: | |
206 | |
207 * [Futures and Error Handling](/articles/futures-and-error-handling/), | |
208 an article that starts where this one ends | |
209 * [The Event Loop and Dart](/articles/event-loop/), | |
210 an article that describes how to schedule tasks using Futures | |
211 * [Future API reference](http://api.dartlang.org/dart_async/Future.html) | |
212 | |
OLD | NEW |