Skip to content

Commit 8134937

Browse files
committed
working
1 parent 1b03278 commit 8134937

File tree

12 files changed

+303
-117
lines changed

12 files changed

+303
-117
lines changed

8-async/01-callback-hell/article.md

Lines changed: 38 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,38 @@ The most obvious example is `setTimeout`, but there are others, like making netw
99

1010
## Callbacks
1111

12-
Consider a function `loadScript` that loads a script:
12+
Consider this function `loadScript(src)` that loads a script:
1313

1414
```js
1515
function loadScript(src) {
1616
let script = document.createElement('script');
1717
script.src = src;
18+
document.head.append(script);
1819
}
1920
```
2021

21-
When the `<script>` tag is created and `src` is assigned, the browser loads the script and executes it. So, the function works. We can use it like this:
22+
When the script element is added to the document, the browser loads it and executes. So, the function works.
23+
24+
We can use it like this:
2225

2326
```js
27+
// loads and executes the script
2428
loadScript('/my/script.js');
2529
```
2630

27-
The function is asynchronous: the script starts loading now, but finishes later, maybe after a few seconds. So, the question is: how can we track the load end? As of now, the function provides no such way.
28-
29-
We'd like to invoke our code after the script is loaded. One of the easiest ways is to add a second argument to `loadScript`: the function that would run on load end.
30-
31+
The function is asynchronous: the script starts loading now, but finishes later.
3132

32-
How can hook on "load completion"?
33+
```smart header="Synchronous vs asynchronous"
34+
"Synchonous" and "asynchronous" are general programming terms, not specific to JavaScript.
3335
36+
A *synchronous* action suspends the execution until it's completed. For instance, `alert` and `prompt` are synchronous: the program may not continue until they are finished.
3437
35-
From ancient times, Javascript allowed to use callback functions for asynchronous
36-
Most asychronous
37-
In this chapter we cover how to write callback-based asynchronous code.
38-
Let's see a couple of examples, so that we can discover a problem, and then solve it using "promises".
39-
40-
41-
Remember resource load/error events? They are covered in the chapter <info:onload-onerror>.
38+
An *asynchronous* action allows the program to continue while it's in progress. For instance, `loadScript` in the example above initiates the script loading, but does not suspend the execution. Other commands may execute while the script is loading.
39+
```
4240

43-
Let's say we want to create a function `loadScript` that loads a script and executes our code afterwards.
41+
As of now, `loadScript` provides no way to track the load end. How can we execute our own code after the script is loaded?
4442

45-
It can look like this:
43+
Let's allow that by adding a custom function as a second argument to `loadScript`, that should execute at that moment:
4644

4745
```js
4846
function loadScript(src, callback) {
@@ -63,25 +61,24 @@ loadScript('/my/script.js', function(script) {
6361
});
6462
```
6563

66-
...And it works, shows `alert` after the script is loaded.
67-
68-
That is called "a callback API". Our function `loadScript` performs an asynchronous task and we can hook on its completion using the callback function.
64+
...And it works, shows the `alert` after the script is loaded.
6965

7066
## Callback in callback
7167

72-
What if we need to load two scripts: one more after the first one?
68+
What if we need to load two scripts sequentially: the first one, and then the second one after it?
7369

74-
We can put another `loadScript` inside the callback, like this:
70+
We can put the second `loadScript` inside the callback, like this:
7571

7672
```js
7773
loadScript('/my/script.js', function(script) {
74+
7875
alert(`Cool, the ${script.src} is loaded, let's load one more`);
7976

77+
*!*
8078
loadScript('/my/script2.js', function(script) {
81-
8279
alert(`Cool, the second script is loaded`);
83-
8480
});
81+
*/!*
8582

8683
});
8784
```
@@ -96,11 +93,11 @@ loadScript('/my/script.js', function(script) {
9693
loadScript('/my/script2.js', function(script) {
9794

9895
if (something) {
99-
loadScript('/my/script3.js', function(script) {
10096
*!*
97+
loadScript('/my/script3.js', function(script) {
10198
// ...continue after all scripts are loaded
102-
*/!*
10399
});
100+
*/!*
104101
}
105102

106103
})
@@ -114,7 +111,7 @@ As you can see, a new asynchronous action means one more nesting level.
114111

115112
In this example we didn't consider errors. What if a script loading failed with an error? Our callback should be able to react on that.
116113

117-
Here's an improved version of `loadScript` that can handle errors:
114+
Here's an improved version of `loadScript` that tracks loading errors:
118115

119116
```js run
120117
function loadScript(src, callback) {
@@ -138,32 +135,34 @@ loadScript('/my/script.js', function(error, script) {
138135
if (error) {
139136
// handle error
140137
} else {
141-
// script loaded, go on
138+
// script loaded successfully
142139
}
143140
});
144141
```
145142

146-
The first argument of `callback` is reserved for errors and the second argument for the successful result. That allows to use a single function to pass both success and failure.
143+
The first argument of `callback` is reserved for errors, and the second argument is for the successful result.
147144

148145
## Pyramid of doom
149146

150-
From the first look it's a viable code. And indeed it is. For one or maybe two nested calls it looks fine.
147+
What we've just seen is called a "callback-based" approach to asynchronous programming. We pass a function, and it should run after the process is complete: with an error or a successful result.
148+
149+
From the first look it's a viable way of asynchronous coding. And indeed it is. For one or maybe two nested calls it looks fine.
151150

152-
But for multiple asynchronous actions that follow one after another...
151+
But for multiple asynchronous actions that follow one after another we'll have a code like this:
153152

154153
```js
155-
loadScript('/my/script1.js', function(error, script) {
154+
loadScript('1.js', function(error, script) {
156155

157156
if (error) {
158157
handleError(error);
159158
} else {
160159
// ...
161-
loadScript('/my/script2.js', function(error, script) {
160+
loadScript('2.js', function(error, script) {
162161
if (error) {
163162
handleError(error);
164163
} else {
165164
// ...
166-
loadScript('/my/script3.js', function(error, script) {
165+
loadScript('3.js', function(error, script) {
167166
if (error) {
168167
handleError(error);
169168
} else {
@@ -180,35 +179,16 @@ loadScript('/my/script1.js', function(error, script) {
180179
```
181180

182181
In the code above:
183-
1. we load `/my/script1.js`, then if there's no error
184-
2. we load `/my/script2.js`, then if there's no error
185-
3. we load `/my/script3.js`, then if there's no error -- do something else `(*)`.
182+
1. We load `1.js`, then if there's no error.
183+
2. We load `2.js`, then if there's no error.
184+
3. We load `3.js`, then if there's no error -- do something else `(*)`.
186185

187-
The nested calls become increasingly more difficult to manage, especially if we add real code instead of `...`, that may include more loops, conditional statements and other usage of loaded scripts.
186+
As calls become more nested, the whole thing becomes increasingly more difficult to manage, especially if we add real code instead of `...`, that may include more loops, conditional statements and other usage of loaded scripts.
188187

189188
That's sometimes called "callback hell" or "pyramid of doom".
190189

191190
![](callback-hell.png)
192191

193-
See? It grows right with every asynchronous action.
194-
195-
Compare that with a "regular" synchronous code.
196-
197-
Just *if* `loadScript` were a regular synchronous function:
198-
199-
```js
200-
try {
201-
// assume we get the result in a synchronous manner, without callbacks
202-
let script = loadScript('/my/script.js');
203-
// ...
204-
let script2 = loadScript('/my/script2.js');
205-
// ...
206-
let script3 = loadScript('/my/script3.js');
207-
} catch(err) {
208-
handleError(err);
209-
}
210-
```
211-
212-
How much cleaner and simpler it is!
192+
The pyramid grows to the right with every asynchronous action. Soon it spirales out of control.
213193

214-
Promises allow to write asynchronous code in a similar way. They are really great at that. Let's study them in the next chapter.
194+
Fortunately, there are ways to evade such pyramids. One of them is using "promises", we'll study them in the next chapters.
1.67 KB
Loading
3.38 KB
Loading

0 commit comments

Comments
 (0)