Skip to content

Commit 6f36a02

Browse files
committed
Updating text
1 parent cfefeba commit 6f36a02

File tree

1 file changed

+52
-54
lines changed

1 file changed

+52
-54
lines changed

control-flow/index.md

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ author: donovan
55
date: Last Modified
66
---
77

8-
As we create programs we write sequences of events. Usually, we expect them execute in order but this is not always the way it occurs. Even though JavaScript ios [single-threaded](https://medium.com/better-programming/is-node-js-really-single-threaded-7ea59bcc8d64 'Is Node.js Really Single-Threaded?'), functions that call APIs, setTimeouts and other structures can result in our code running in an unexpected order.
8+
JavaScript programs are made up of series of instructions. When our programs run, they run in sequence from the top down, one line at a time. Most lines of are _synchronous_, meaning they run in order. However not all do. Sometimes _asynchronous_ code can cause our code to execute in an unexpected order.
99

1010
Consider the following code.
1111

@@ -21,6 +21,8 @@ In the above code our compiler goes through each line executing each instruction
2121

2222
This is not what we want. We'll have to go wash our hands again. In production code, even worse things can happen.
2323

24+
Node generally runs in one single thread and any _blocking_ code will be run in sequence, with _non-blocking_ code having the potential to run _asynchronously_.
25+
2426
Thankfully Node offers 3 approaches we can use to control the order in which our code executes. _Callbacks_, _promises_ and _async/await_. Let's take a look at each.
2527

2628
## Callbacks
@@ -34,28 +36,28 @@ To simulate this I've created a `randomDelayedResponse` function that will retur
3436
```
3537
function randomDelayedResponse(text) {
3638
// Using a timeout here for the sake of simulating an external request
37-
const timeOut = Math.floor(Math.random() * 10) + 1;
39+
const timeOut = Math.floor(Math.random() * 100) + 1;
3840
const output = text;
3941
setTimeout(() => {
4042
return output;
41-
}, timeOut * 10);
43+
}, timeOut);
4244
}
4345
4446
const output = randomDelayedResponse('Hello');
45-
console.log(output); // empty
47+
console.log(output); // undefined
4648
```
4749

48-
Notice that the final line above returns `undefined`.
50+
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.
4951

5052
We need to wait until the response is ready. One way is to pass our `console.log` in to `randomDelayedResponse` as a function.
5153

5254
```
5355
function randomDelayedResponse(text, callback) {
5456
// Using a timeout here for the sake of simulating an external request
55-
const timeOut = Math.floor(Math.random() * 10) + 1;
57+
const timeOut = Math.floor(Math.random() * 100) + 1;
5658
setTimeout(() => {
5759
callback(text);
58-
}, timeOut * 10);
60+
}, timeOut);
5961
}
6062
6163
const output = randomDelayedResponse('Hello', text => console.log(text)); // outputs "Hello"
@@ -69,30 +71,26 @@ This might be reasonable in simple situations but it's easy for callbacks to get
6971
To show many responses, consider the following code. If we ran them beside each other, the output would not be reliable.
7072

7173
```
72-
function randomDelayedResponse(text, callback) {
73-
// Using a timeout here for the sake of simulating an external request
74-
const timeOut = Math.floor(Math.random() * 10) + 1;
75-
setTimeout(() => {
76-
callback(text);
77-
}, timeOut * 10);
78-
}
74+
// ... replacing the last two lines of the previous example
7975
80-
randomDelayedResponse(1, text => console.log(text)); // outputs 1
81-
randomDelayedResponse(2, text => console.log(text)); // outputs 2
82-
randomDelayedResponse(3, text => console.log(text)); // outputs 3
83-
randomDelayedResponse(4, text => console.log(text)); // outputs 4
76+
randomDelayedResponse(1, console.log); // outputs 1
77+
randomDelayedResponse(2, console.log); // outputs 2
78+
randomDelayedResponse(3, console.log); // outputs 3
79+
randomDelayedResponse(4, console.log); // outputs 4
8480
// Who will win?
8581
```
8682

87-
We can run the above code multiple times, and the result is unpredictable. It 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.
83+
Here we pass in the function `console.log` as the second argument which is then run as `callback(text)` to log the output.
84+
85+
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.
8886

8987
```
9088
function randomDelayedResponse(text, callback) {
9189
// Using a timeout here for the sake of simulating an external request
92-
const timeOut = Math.floor(Math.random() * 10) + 1;
90+
const timeOut = Math.floor(Math.random() * 100) + 1;
9391
setTimeout(() => {
9492
callback(text);
95-
}, timeOut * 10);
93+
}, timeOut);
9694
}
9795
9896
randomDelayedResponse(1, text => {
@@ -109,7 +107,9 @@ randomDelayedResponse(1, text => {
109107
}); // outputs "1 2 3 4"
110108
```
111109

112-
Structuring these callbacks works. However this approach can create code becomes difficult to understand and maintain. Let's look at another way.
110+
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.
111+
112+
Let's look at one such alternative.
113113

114114
## Promises
115115

@@ -133,10 +133,13 @@ We can rewrite our runner example using promises like so:
133133
```
134134
function randomDelayedResponse(text, callback) {
135135
return new Promise((resolve, reject) => {
136-
const timeOut = Math.floor(Math.random() * 10) + 1;
136+
const timeOut = Math.floor(Math.random() * 100) + 1;
137137
setTimeout(() => {
138-
resolve(text); // Replacing the callback with "resolve"
139-
}, timeOut * 10);
138+
if (text) {
139+
resolve(text); // Replacing the callback with "resolve"
140+
}
141+
reject('No text provided!'); // Reject when there is an error
142+
}, timeOut);
140143
});
141144
}
142145
@@ -157,32 +160,23 @@ randomDelayedResponse(1)
157160
console.log(res);
158161
})
159162
.catch(error => {
160-
// handle error
163+
console.log(error);
161164
})
162165
// outputs "1 2 3 4"
163166
```
164167

165168
Running the above code should output the correct order, albeit with random delays between each number.
166169

167-
We aren't handling errors in this example, but if our function was to encounter an error we would `reject('some error')` and this would then be picked up by the `catch`.
170+
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.
168171

169-
You can learn more about `promises` here (LINK)
170-
171-
Promises remove a lot of nesting and give us easier to read code. However there is a third way to control the execution order of our code.
172+
Promises remove the nesting and give us easier to read code. Let's look at a third way that builds on this.
172173

173174
## Async / Await
174175

175-
The third approach is built on top of the existing _promises_ approach but makes it easier to reason with. With `async` and `await` we can write code that feels a lot more like the simple top-down code, by telling it to wait when we need it to. Let's rewrite our _who will win?_ example from above.
176+
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.
176177

177178
```
178-
function randomDelayedResponse(text, callback) {
179-
return new Promise((resolve, reject) => {
180-
const timeOut = Math.floor(Math.random() * 10) + 1;
181-
setTimeout(() => {
182-
resolve(text);
183-
}, timeOut * 10);
184-
});
185-
}
179+
// Replace the second part of the example above
186180
187181
async function runTheRace() { // put `async` before the function call
188182
@@ -200,27 +194,31 @@ async function runTheRace() { // put `async` before the function call
200194
runTheRace();
201195
```
202196

203-
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. This way, the code execution order is preserved.
197+
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.
204198

205199
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 }}).
206200

201+
## Which is preferred?
202+
203+
Generally you should try to aim for the `async/await` approach when possible. It helps promote clean code. None of the approaches are to be avoided entirely, but in general always aim for the approach that ensures the code is easy to read and understand.
204+
207205
## Exercise
208206

209207
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?
210208

211209
```
212210
function hello() {
213-
const timeOut = Math.floor(Math.random() * 10) + 1;
211+
const timeOut = Math.floor(Math.random() * 100) + 1;
214212
setTimeout(() => {
215213
console.log('hello');
216-
}, timeOut * 10);
214+
}, timeOut);
217215
}
218216
219217
function world() {
220-
const timeOut = Math.floor(Math.random() * 10) + 1;
218+
const timeOut = Math.floor(Math.random() * 100) + 1;
221219
setTimeout(() => {
222220
console.log('world');
223-
}, timeOut * 10);
221+
}, timeOut);
224222
}
225223
```
226224

@@ -233,11 +231,11 @@ function world() {
233231
```
234232
# Callbacks
235233
function hello(callback) {
236-
const timeOut = Math.floor(Math.random() * 10) + 1;
234+
const timeOut = Math.floor(Math.random() * 100) + 1;
237235
setTimeout(() => {
238236
console.log('hello');
239237
callback();
240-
}, timeOut * 10);
238+
}, timeOut);
241239
}
242240
243241
function world() {
@@ -255,19 +253,19 @@ hello(world);
255253
256254
function hello() {
257255
return new Promise((resolve, reject) => {
258-
const timeOut = Math.floor(Math.random() * 10) + 1;
256+
const timeOut = Math.floor(Math.random() * 100) + 1;
259257
setTimeout(() => {
260258
resolve('hello');
261-
}, timeOut * 10);
259+
}, timeOut);
262260
});
263261
}
264262
265263
function world() {
266264
return new Promise((resolve, reject) => {
267-
const timeOut = Math.floor(Math.random() * 10) + 1;
265+
const timeOut = Math.floor(Math.random() * 100) + 1;
268266
setTimeout(() => {
269267
resolve('world');
270-
}, timeOut * 10);
268+
}, timeOut);
271269
});
272270
}
273271
@@ -287,19 +285,19 @@ hello()
287285
288286
function hello() {
289287
return new Promise((resolve, reject) => {
290-
const timeOut = Math.floor(Math.random() * 10) + 1;
288+
const timeOut = Math.floor(Math.random() * 100) + 1;
291289
setTimeout(() => {
292290
resolve('hello');
293-
}, timeOut * 10);
291+
}, timeOut);
294292
});
295293
}
296294
297295
function world() {
298296
return new Promise((resolve, reject) => {
299-
const timeOut = Math.floor(Math.random() * 10) + 1;
297+
const timeOut = Math.floor(Math.random() * 100) + 1;
300298
setTimeout(() => {
301299
resolve('world');
302-
}, timeOut * 10);
300+
}, timeOut);
303301
});
304302
}
305303

0 commit comments

Comments
 (0)