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: 1-js/12-generators-iterators/2-async-iterators-generators/article.md
+41-31
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,13 @@
1
1
2
-
# Async iterators and generators
2
+
# Async iteration and generators
3
3
4
-
Asynchronous iterators allow us to iterate over data that comes asynchronously, on-demand. Like, for instance, when we download something chunk-by-chunk over a network. And asynchronous generators make it even more convenient.
4
+
Asynchronous iteration allow us to iterate over data that comes asynchronously, on-demand. Like, for instance, when we download something chunk-by-chunk over a network. And asynchronous generators make it even more convenient.
5
5
6
6
Let's see a simple example first, to grasp the syntax, and then review a real-life use case.
7
7
8
-
## Recall iterators
8
+
## Recall iterables
9
9
10
-
Let's recall the topic about iterators.
10
+
Let's recall the topic about iterables.
11
11
12
12
The idea is that we have an object, such as `range` here:
13
13
```js
@@ -17,17 +17,17 @@ let range = {
17
17
};
18
18
```
19
19
20
-
...And we'd like to use `for..of` loop on it, such as `for(value of range)`, to get values from `1` to `5`. And otherwise use the object, as if it were an array.
20
+
...And we'd like to use `for..of` loop on it, such as `for(value of range)`, to get values from `1` to `5`.
21
21
22
22
In other words, we want to add an *iteration ability* to the object.
23
23
24
24
That can be implemented using a special method with the name `Symbol.iterator`:
25
25
26
-
- This method is called in the beginning of a `for..of` loop, and it should return an object with the `next` method.
27
-
- For each iteration of `for..of`, the `next()` method is invoked for the next value.
28
-
- The `next()` should return a value in the form `{done: true/false, value:<loop value>}`.
26
+
- This method is called in by the `for..of`construct when the loop is started, and it should return an object with the `next` method.
27
+
- For each iteration, the `next()` method is invoked for the next value.
28
+
- The `next()` should return a value in the form `{done: true/false, value:<loop value>}`, where `done:true` means the end of the loop.
29
29
30
-
Here's an implementation for the `range`, with all the comments:
30
+
Here's an implementation for the iterable `range`:
31
31
32
32
```js run
33
33
let range = {
@@ -59,19 +59,17 @@ for(let value of range) {
59
59
}
60
60
```
61
61
62
-
If anything is unclear, please visit the [chapter about iterables](info:iterable), it gives all the details about regular iterators.
62
+
If anything is unclear, please visit the chapter [](info:iterable), it gives all the details about regular iterables.
63
63
64
-
## Async iterators
64
+
## Async iterables
65
65
66
-
Asynchronous iterators are similar to regular iterators. We also need to have an iterable object, but values are expected to come asynchronously.
66
+
Asynchronous iteration is needed when values come asynchronously: after `setTimeout` or another kind of delay.
67
67
68
-
The most common case is that the object needs to make a network request to deliver the next value.
68
+
The most common case is that the object needs to make a network request to deliver the next value, we'll see a real-life example of it a bit later.
69
69
70
-
Regular iterators, as the one above, require `next()` to return the next value right away. That's where asynchronous iterators come into play.
70
+
To make an object iterable asynchronously:
71
71
72
-
To make the object iterable asynchronously:
73
-
74
-
1. We need to use `Symbol.asyncIterator` instead of `Symbol.iterator`.
72
+
1. Use `Symbol.asyncIterator` instead of `Symbol.iterator`.
75
73
2. The `next()` method should return a promise (to be fulfilled with the next value).
76
74
- The `async` keyword handles it, we can simply make `async next()`.
77
75
3. To iterate over such an object, we should use a `for await (let item of iterable)` loop.
@@ -153,9 +151,9 @@ It's also the case for `for..of`: the syntax without `await` needs `Symbol.itera
153
151
154
152
## Recall generators
155
153
156
-
Now let's recall generators. They are explained in detail in the chapter [](info:generators).
154
+
Now let's recall generators, as they allow to make iteration code much shorter. Most of the time, when we'd like to make an iterable, we'll use generators.
157
155
158
-
For sheer simplicity, omitting some important stuff, they are "functions that generate (yield) values".
156
+
For sheer simplicity, omitting some important stuff, they are "functions that generate (yield) values". They are explained in detail in the chapter [](info:generators).
159
157
160
158
Generators are labelled with `function*` (note the start) and use `yield` to generate a value, then we can use `for..of` to loop over them.
161
159
@@ -187,7 +185,7 @@ let range = {
187
185
}
188
186
```
189
187
190
-
A common practice for `Symbol.iterator` is to return a generator, it makes the code shorter:
188
+
A common practice for `Symbol.iterator` is to return a generator, it makes the code shorter, as you can see:
191
189
192
190
```js run
193
191
let range = {
@@ -208,15 +206,19 @@ for(let value of range) {
208
206
209
207
Please see the chapter [](info:generators) if you'd like more details.
210
208
211
-
Once again, what if we'd like to generate values asynchronously? From network requests, for instance.
212
-
213
209
In regular generators we can't use `await`. All values must come synchronously, as required by the `for..of` construct.
214
210
215
-
Let's switch to asynchronous generators, to make it possible.
211
+
What if we'd like to generate values asynchronously? From network requests, for instance.
212
+
213
+
Let's switch to asynchronous generators to make it possible.
216
214
217
-
## Async generators
215
+
## Async generators (finally)
218
216
219
-
To make an asynchronous generator, prepend `function*` with `async`, like this:
217
+
For most practical applications, when we'd like to make an object that asynchronously generates a sequence of values, we can use an asynchronous generator.
218
+
219
+
The syntax is simple: prepend `function*` with `async`. That makes the generator asynchronous.
220
+
221
+
And then use `for await (...)` to iterate over it, like this:
@@ -237,15 +239,13 @@ To make an asynchronous generator, prepend `function*` with `async`, like this:
237
239
238
240
let generator = generateSequence(1, 5);
239
241
for *!*await*/!* (let value of generator) {
240
-
alert(value); // 1, then 2, then 3, then 4, then 5
242
+
alert(value); // 1, then 2, then 3, then 4, then 5 (with delay between)
241
243
}
242
244
243
245
})();
244
246
```
245
247
246
-
Now we have the async generator, iterable with `for await...of`.
247
-
248
-
It's really simple. We add the `async` keyword, and the generator now can use `await` inside of it, rely on promises and other async functions.
248
+
As the generator is asynchronous, we can use `await` inside it, rely on promises, perform network requests and so on.
249
249
250
250
````smart header="Under-the-hood difference"
251
251
Technically, if you're an advanced reader who remembers the details about generators, there's an internal difference.
@@ -260,7 +260,13 @@ result = await generator.next(); // result = {value: ..., done: true/false}
260
260
That's why async generators work with `for await...of`.
261
261
````
262
262
263
-
We can make the `range` object generate values asynchronously, once per second, by replacing synchronous `Symbol.iterator` with asynchronous `Symbol.asyncIterator`:
263
+
### Async iterable range
264
+
265
+
Regular generators can be used as `Symbol.iterator` to make the iteration code shorter.
266
+
267
+
Similar to that, async generators can be used as `Symbol.asyncIterator` to implement the asynchronous iteration.
268
+
269
+
For instance, we can make the `range` object generate values asynchronously, once per second, by replacing synchronous `Symbol.iterator` with asynchronous `Symbol.asyncIterator`:
264
270
265
271
```js run
266
272
let range = {
@@ -292,7 +298,11 @@ let range = {
292
298
293
299
Now values come with a delay of 1 second between them.
294
300
295
-
So, we can make any object asynchronously iterable by adding an async generator as its `Symbol.asyncIterator` method, and letting it to generate values.
301
+
```smart
302
+
Technically, we can add both `Symbol.iterator` and `Symbol.asyncIterator` to the object, so it's both synchronously (`for..of`) and asynchronously (`for await..of`) iterable.
303
+
304
+
In practice though, that would be an weird thing to do.
0 commit comments