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
Of course, in real life there would most likely be additional lines of code to handle `result1`, `result2`, etc., thus, the length and complexity of this issue usually results in code that looks much more messy than the example above.
32
32
33
33
## The Modern Solution: [async/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
34
-
To solve callback hell, modern JavaScript introduced async and await. This is syntactic sugar built on top of a concept called Promises, allowing you to write asynchronous code that looks and behaves like synchronous code. It makes complex flows much easier to reason about.
35
34
36
-
* An async function is a function that implicitly returns a Promise.
35
+
To solve callback hell, modern JavaScript introduced async and await. This is syntactic sugar built on top of a concept called Promises, allowing you to write asynchronous code that looks and behaves like synchronous code. It makes complex flows much easier to reason about.
37
36
38
-
* The await keyword can only be used inside an async function. It pauses the function's execution and waits for a Promise to be resolved, then resumes with the resolved value.
37
+
- An async function is a function that implicitly returns a Promise.
39
38
39
+
- The await keyword can only be used inside an async function. It pauses the function's execution and waits for a Promise to be resolved, then resumes with the resolved value.
40
40
41
41
Let's rewrite the "callback hell" example using async/await. Notice how flat and readable it becomes:
42
+
42
43
```js
43
44
asyncfunctionperformOperations() {
44
45
try {
@@ -54,6 +55,7 @@ async function performOperations() {
54
55
}
55
56
}
56
57
```
58
+
57
59
This code is executed sequentially from top to bottom, just like synchronous code, but without blocking the main thread.
58
60
59
61
**Thinking in Functions**
@@ -75,6 +77,7 @@ Network requests can be incoming requests initiated by a foreign network, by ano
75
77
A middleware function will return another function, and a terminator function will invoke the callback. The following illustrates the flow to network or file system requests. Here the latency is 0 because all these values are available in memory.
76
78
77
79
Here’s how we can structure a simple flow using async functions. Each function passes its result to the next.
80
+
78
81
```js
79
82
asyncfunctionfinal(someInput) {
80
83
return`${someInput} and terminated.`;
@@ -86,7 +89,7 @@ async function middleware(someInput) {
86
89
}
87
90
88
91
asyncfunctioninitiate() {
89
-
constsomeInput='hello this is a function, ';
92
+
constsomeInput="hello this is a function, ";
90
93
// We await the result of the entire chain.
91
94
constresult=awaitmiddleware(someInput);
92
95
console.log(result);
@@ -113,7 +116,7 @@ If an object is available in memory, iteration is possible, and there will not b
113
116
114
117
```js
115
118
functiongetSong() {
116
-
let _song ='';
119
+
let _song ="";
117
120
let i =100;
118
121
for (i; i >0; i -=1) {
119
122
_song +=`${i} beers on the wall, you take one down and pass it around, ${i -1} bottles of beer on the wall\n`;
@@ -139,7 +142,7 @@ However, if the data exists outside of memory the iteration will no longer work:
139
142
140
143
```js
141
144
functiongetSong() {
142
-
let _song ='';
145
+
let _song ="";
143
146
let i =100;
144
147
for (i; i >0; i -=1) {
145
148
setTimeout(function () {
@@ -158,13 +161,13 @@ function singSong(_song) {
158
161
console.log(_song);
159
162
}
160
163
161
-
constsong=getSong('beer');
164
+
constsong=getSong("beer");
162
165
// this will not work
163
166
singSong(song);
164
167
// Uncaught Error: song is '' empty, FEED ME A SONG!
165
168
```
166
169
167
-
Why did this happen? setTimeout (like file system or network requests) instructs the Node.js event loop to schedule the provided function for execution at a later time. The for loop completes almost instantly, and _song (which is still an empty string) is returned immediately. The functions scheduled by setTimeout run much later, long after singSong has attempted to use the empty _song.
170
+
Why did this happen? setTimeout (like file system or network requests) instructs the Node.js event loop to schedule the provided function for execution at a later time. The for loop completes almost instantly, and \_song (which is still an empty string) is returned immediately. The functions scheduled by setTimeout run much later, long after singSong has attempted to use the empty \_song.
168
171
169
172
The main thread cannot be blocked indefinitely while waiting for I/O or other asynchronous tasks. Fortunately, Promises and async/await provide the mechanisms to explicitly wait for these operations to complete before continuing, allowing us to manage asynchronous control flow effectively.
170
173
@@ -174,13 +177,14 @@ You will be able to perform almost all of your operations with the following 3 p
174
177
175
178
```js
176
179
// Simulate an asynchronous operation and return a Promise.
@@ -202,14 +206,15 @@ async function executeInSeries(asyncFunctions) {
202
206
awaitexecuteInSeries(operations);
203
207
})();
204
208
```
209
+
205
210
**Applying "In Series": The Beer Song Solution:** The "In Series" pattern is precisely what's needed to fix the song generation, it makes sure that each line is created then added in the correct order.
206
211
207
212
```js
208
213
asyncfunctiongetSong() {
209
214
const_songParts= [];
210
215
for (let i =100; i >0; i -=1) {
211
216
// Await for each line.
212
-
constline=awaitnewPromise(resolve=> {
217
+
constline=awaitnewPromise((resolve)=> {
213
218
setTimeout(() => {
214
219
let currentLine =`${i} beers on the wall, you take one down and pass it around, ${
215
220
i -1
@@ -222,7 +227,7 @@ async function getSong() {
222
227
});
223
228
_songParts.push(line);
224
229
}
225
-
return_songParts.join('');
230
+
return_songParts.join("");
226
231
}
227
232
228
233
functionsingSong(songContent) {
@@ -242,14 +247,15 @@ function singSong(songContent) {
@@ -259,7 +265,9 @@ async function processLimitedInSeries(items, limit) {
259
265
constqueue= [...items];
260
266
constactive=newSet(); // Tracks currently running promises.
261
267
262
-
console.log(`\n--- Starting Limited In Series Execution (Limit: ${limit}) ---`);
268
+
console.log(
269
+
`\n--- Starting Limited In Series Execution (Limit: ${limit}) ---`,
270
+
);
263
271
264
272
while (queue.length>0||active.size>0) {
265
273
while (active.size< limit &&queue.length>0) {
@@ -326,8 +334,8 @@ Each has its own use cases, benefits, and issues you can experiment and read abo
326
334
327
335
### Choosing the Right Pattern
328
336
329
-
***In Series:** Use when order is critical, or when each task depends on the previous one (e.g., chained database operations).
337
+
-**In Series:** Use when order is critical, or when each task depends on the previous one (e.g., chained database operations).
330
338
331
-
***Limited in Series:** Use when you need concurrency but must control resource load or adhere to external rate limits (e.g., API calls to a throttled service).
339
+
-**Limited in Series:** Use when you need concurrency but must control resource load or adhere to external rate limits (e.g., API calls to a throttled service).
332
340
333
-
***Full Parallel:** Use when operations are independent and can run simultaneously for maximum throughput (e.g., fetching data from multiple unrelated sources).
341
+
-**Full Parallel:** Use when operations are independent and can run simultaneously for maximum throughput (e.g., fetching data from multiple unrelated sources).
0 commit comments