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: 8-async/01-callback-hell/article.md
+38-58Lines changed: 38 additions & 58 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,40 +9,38 @@ The most obvious example is `setTimeout`, but there are others, like making netw
9
9
10
10
## Callbacks
11
11
12
-
Consider a function `loadScript` that loads a script:
12
+
Consider this function `loadScript(src)` that loads a script:
13
13
14
14
```js
15
15
functionloadScript(src) {
16
16
let script =document.createElement('script');
17
17
script.src= src;
18
+
document.head.append(script);
18
19
}
19
20
```
20
21
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:
22
25
23
26
```js
27
+
// loads and executes the script
24
28
loadScript('/my/script.js');
25
29
```
26
30
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.
31
32
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.
33
35
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.
34
37
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
+
```
42
40
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?
44
42
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:
...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.
69
65
70
66
## Callback in callback
71
67
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?
73
69
74
-
We can put another`loadScript` inside the callback, like this:
70
+
We can put the second`loadScript` inside the callback, like this:
75
71
76
72
```js
77
73
loadScript('/my/script.js', function(script) {
74
+
78
75
alert(`Cool, the ${script.src} is loaded, let's load one more`);
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.
147
144
148
145
## Pyramid of doom
149
146
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.
151
150
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:
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 `(*)`.
186
185
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.
188
187
189
188
That's sometimes called "callback hell" or "pyramid of doom".
190
189
191
190

192
191
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.
213
193
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.
0 commit comments