You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: control-flow/index.md
+55-8Lines changed: 55 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,8 @@ JavaScript programs are made up of series of instructions. When our programs run
9
9
10
10
Consider the following code.
11
11
12
+
<divclass="repl-code">
13
+
12
14
```
13
15
console.log('Clean teeth');
14
16
setTimeout(() => {
@@ -17,6 +19,8 @@ setTimeout(() => {
17
19
console.log('Wash face and hands');
18
20
```
19
21
22
+
</div>
23
+
20
24
In the above code our compiler goes through each line executing each instruction. First it logs `Clean teeth`. Then it doesn't hang around for the `setTimeout`, it simply carries on and logs `Wash face and hands`. Then (a small moment later), it will log `Use lavatory`.
21
25
22
26
This is not what we want. We'll have to go wash our hands again. In production code, even worse things can happen.
@@ -33,6 +37,8 @@ Let's say we have to get some data from an external service. We don't know how l
33
37
34
38
To simulate this I've created a `randomDelayedResponse` function that will return a response after an unspecified amount of time.
35
39
40
+
<divclass="repl-code">
41
+
36
42
```
37
43
function randomDelayedResponse(text) {
38
44
// Using a timeout here for the sake of simulating an external request
Notice that the final line above returns `undefined`. This is because the `randomDelayedResponse` line is _non_blocking_ and hasn't returned anything to `output` before we then try to apply `console.log` to it.
51
59
52
60
We need to wait until the response is ready. One way is to pass our `console.log` in to `randomDelayedResponse` as a function.
53
61
62
+
<divclass="repl-code">
63
+
54
64
```
55
65
function randomDelayedResponse(text, callback) {
56
66
// Using a timeout here for the sake of simulating an external request
@@ -64,12 +74,16 @@ const output = randomDelayedResponse('Hello', text => console.log(text)); // out
64
74
console.log(output); // still empty
65
75
```
66
76
77
+
</div>
78
+
67
79
We pass the function `text => console.log(text)` as the second parameter, and this function is then called after the `setTimeout`.
68
80
69
81
This might be reasonable in simple situations but it's easy for callbacks to get out of control. For example, if we have many different calls to this slow function that need to be run in order. To achieve this control we need to nest further.
70
82
71
83
To show many responses, consider the following code. If we ran them beside each other, the output would not be reliable.
Here we pass in the function `console.log` as the second argument which is then run as `callback(text)` to log the output.
89
105
90
106
While the code does run in order and run the `randomDelayedResponse` function with the right sequence of inputs, the random delays mean they won't be logged in order. We can run the above code multiple times and the result is never predictable. Each call to the function is _asynchronous_, in that the results do not arrive in order. To create a predictable, _synchronous_ flow using callbacks we'd need to nest them.
@@ -111,6 +129,8 @@ randomDelayedResponse(1, text => {
111
129
}); // outputs "1 2 3 4"
112
130
```
113
131
132
+
</div>
133
+
114
134
Structuring these callbacks outputs the numbers in the correct order. However when we use `callbacks` the code can become difficult to understand and maintain. This might be suitable in simple cases. Though if we find ourselves nesting multiple levels deep we should look for other ways to control the flow.
115
135
116
136
Let's look at one such alternative.
@@ -134,6 +154,8 @@ ourPromiseFunction() // returns a promise that will resolve or reject
134
154
135
155
We can rewrite our runner example using promises like so:
136
156
157
+
<divclass="repl-code">
158
+
137
159
```
138
160
function randomDelayedResponse(text, callback) {
139
161
return new Promise((resolve, reject) => {
@@ -169,6 +191,8 @@ randomDelayedResponse(1)
169
191
// outputs "1 2 3 4"
170
192
```
171
193
194
+
</div>
195
+
172
196
Running the above code should output the correct order, albeit with random delays between each number.
173
197
174
198
In the above example we shouldn't see any errors - but if you want to adjust one of the `andomDelayedResponse()` calls to pass in no data, it should `reject` and the `catch` block will log the error.
@@ -179,6 +203,8 @@ Promises remove the nesting and give us easier to read code. Let's look at a thi
179
203
180
204
The third approach is built on top of the existing _promises_ approach and results in even simpler code. With `async` and `await` we can write code that feels a lot more like our usual top-down code. It works by telling our commands to wait when we need them to. Let's rewrite our _who will win?_ example from above.
181
205
206
+
<divclass="repl-code">
207
+
182
208
```
183
209
function randomDelayedResponse(text, callback) {
184
210
return new Promise((resolve, reject) => {
@@ -215,6 +241,8 @@ async function runTheRace() { // put `async` before the function call
215
241
runTheRace();
216
242
```
217
243
244
+
</div>
245
+
218
246
In the above we have replaced the series of `.then()` calls with an `async` function. It's still making use of a _promise_ but when we want to wait for the promise we simply place `await` before the asynchronous function. The code execution order is preserved.
219
247
220
248
You may be wondering how we handle errors here. One approach is to use the `try/catch` method. We'll be covering that and more in the [error handling section]({{ "/error-handling/" | url }}).
@@ -227,6 +255,8 @@ Generally you should try to aim for the `async/await` approach when possible. It
227
255
228
256
Given the following code, how would you change it so that the results always outputs `hello world`? Can you make it work using all 3 of the above approaches?
0 commit comments