Asynchronous traversalBefore explaining asynchronous traversal, let's recall the synchronous traversal in ES6. According to the definition of ES6, iteration mainly consists of three parts: Iterable First, let's look at the definition of Iterable: interface Iterable { [Symbol.iterator]() : Iterator; } Iterable means that this object contains traversable data and needs to implement a factory method that can generate Iterator. Iterator interface Iterator { next() : IteratorResult; } Iterators can be constructed from Iterables. Iterator is a concept similar to a cursor, and the IteratorResult can be accessed through next. IteratorResult IteratorResult is the data obtained each time the next method is called. interface IteratorResult { value: any; done: boolean; } In addition to a value representing the data to be obtained, IteratorResult also has a done, which indicates whether the traversal is completed. Here is an example of iterating over an array:
However, the above example traverses synchronous data. If we obtain asynchronous data, such as files downloaded from the http end, we want to traverse the file line by line. Because reading a row of data is an asynchronous operation, this involves traversing asynchronous data. Add the asynchronous file reading method readLinesFromFile, then the synchronous traversal method is no longer applicable to asynchronous: //No longer applicable for (const line of readLinesFromFile(fileName)) { console.log(line); } You may wonder, can we encapsulate the operation of asynchronously reading a line in a Promise and then traverse it in a synchronous way? The idea is good, but in this case, it is impossible to detect whether the asynchronous operation has been completed. So this method is not feasible. So ES9 introduced the concept of asynchronous traversal: 1. You can use Symbol.asyncIterator to get the iterator in asynchronous iterables. 2. The next() method of the asynchronous iterator returns a Promises object, which contains IteratorResults. So, let's look at the API definition of asynchronous traversal: interface AsyncIterable { [Symbol.asyncIterator]() : AsyncIterator; } interface AsyncIterator { next() : Promise<IteratorResult>; } interface IteratorResult { value: any; done: boolean; } Let's look at an asynchronous traversal application: const asyncIterable = createAsyncIterable(['a', 'b']); const asyncIterator = asyncIterable[Symbol.asyncIterator](); asyncIterator.next() .then(iterResult1 => { console.log(iterResult1); // { value: 'a', done: false } return asyncIterator.next(); }) .then(iterResult2 => { console.log(iterResult2); // { value: 'b', done: false } return asyncIterator.next(); }) .then(iterResult3 => { console.log(iterResult3); // { value: undefined, done: true } }); Among them, createAsyncIterable will convert a synchronous iterable into an asynchronous iterable. We will see how it is generated in the following section. Here we mainly focus on the traversal operation of asyncIterator. Because ES8 introduces the Async operator, we can also rewrite the above code using the Async function: async function f() { const asyncIterable = createAsyncIterable(['a', 'b']); const asyncIterator = asyncIterable[Symbol.asyncIterator](); console.log(await asyncIterator.next()); // { value: 'a', done: false } console.log(await asyncIterator.next()); // { value: 'b', done: false } console.log(await asyncIterator.next()); // { value: undefined, done: true } } Asynchronous iterable traversalUse for-of to traverse a synchronous iterable, and use for-await-of to traverse an asynchronous iterable. async function f() { for await (const x of createAsyncIterable(['a', 'b'])) { console.log(x); } } // Output: // a // b Note that await needs to be placed in an async function. If an exception occurs in our asynchronous traversal, we can use try catch in for-await-of to catch the exception: function createRejectingIterable() { return { [Symbol.asyncIterator]() { return this; }, next() { return Promise.reject(new Error('Problem!')); }, }; } (async function () { try { for await (const x of createRejectingIterable()) { console.log(x); } } catch (e) { console.error(e); // Error: Problem! } })(); The synchronous iterable returns synchronous iterators, and the next method returns {value, done}. If you use for-await-of, synchronous iterators will be converted into asynchronous iterators. The returned value is then converted into a Promise. If the value returned by the synchronous next itself is a Promise object, the asynchronous return value is still the same promise. That is to say, it will convert: Iterable<Promise<T>> into AsyncIterable<T>, as shown in the following example: async function main() { const syncIterable = [ Promise.resolve('a'), Promise.resolve('b'), ]; for await (const x of syncIterable) { console.log(x); } } main(); // Output: // a // b The above example converts a synchronous Promise into an asynchronous Promise. async function main() { for await (const x of ['a', 'b']) { console.log(x); } } main(); // Output: // c // d The example above converts a synchronous constant into a Promise. You can see that the results are the same. Asynchronous iterable generationGoing back to the example above, we use createAsyncIterable(syncIterable) to convert syncIterable into AsyncIterable. Let's see how this method is implemented: async function* createAsyncIterable(syncIterable) { for (const elem of syncIterable) { yield elem; } } In the above code, we add async in front of a normal generator function, which means it is an asynchronous generator. For ordinary generators, each time the next method is called, an object {value, done} is returned. This object is an encapsulation of the yield value. For an asynchronous generator, each time the next method is called, a promise object containing object {value, done} is returned. This object is an encapsulation of the yield value. Because a Promise object is returned, we do not need to wait for the result of the asynchronous execution to be completed before calling the next method again. We can use a Promise.all to execute all asynchronous Promise operations at the same time: const asyncGenObj = createAsyncIterable(['a', 'b']); const [{value:v1},{value:v2}] = await Promise.all([ asyncGenObj.next(), asyncGenObj.next() ]); console.log(v1, v2); // ab In createAsyncIterable, we create an asynchronous Iterable from a synchronous Iterable. Next, let's look at how to create an asynchronous Iterable from an asynchronous Iterable. From the previous section, we know that we can use for-await-of to read data from asynchronous Iterable, so we can use it like this: async function* prefixLines(asyncIterable) { for await (const line of asyncIterable) { yield '> ' + line; } } In the generator article, we talked about calling generators within generators. That is, in a producer, another generator is called by using yield*. Likewise, we can do the same thing in an asynchronous generator: async function* gen1() { yield 'a'; yield 'b'; return 2; } async function* gen2() { const result = yield* gen1(); // result === 2 } (async function () { for await (const x of gen2()) { console.log(x); } })(); // Output: // a // b If an exception is thrown in an asynchronous generator, the exception will also be wrapped in a Promise: async function* asyncGenerator() { throw new Error('Problem!'); } asyncGenerator().next() .catch(err => console.log(err)); // Error: Problem! Async Methods and Async GeneratorsAn asynchronous method is a method declared using async function, which returns a Promise object. The return or thrown exception in the function will be used as the value in the returned Promise. (async function () { return 'hello'; })() .then(x => console.log(x)); // hello (async function () { throw new Error('Problem!'); })() .catch(x => console.error(x)); // Error: Problem! Asynchronous generators are methods declared using async function *. It returns an asynchronous iterable. By calling the next method of iterable, a Promise will be returned. The value yielded by the asynchronous generator is used to fill the value of the Promise. If an exception is thrown in the generator, it will also be caught by Promise. async function* gen() { yield 'hello'; } const genObj = gen(); genObj.next().then(x => console.log(x)); // { value: 'hello', done: false } The above is a detailed explanation of the new feature of ES9, Async iteration. For more information about the new feature of ES9, Async iteration, please pay attention to other related articles on 123WORDPRESS.COM! You may also be interested in:
|
<<: MySQL max_allowed_packet setting
>>: How to deal with garbled characters in Mysql database
Today we will talk about how to use Jenkins+power...
Free points Interviewer : Have you ever used Linu...
1. Download the MySQL 5.7.11 zip installation pac...
Scenario The company project is deployed in Docke...
In some scenarios, we need to modify our varchar ...
Overview In actual business scenario applications...
When the DataSource property of a DataGrid control...
Today I suddenly thought of reviewing the producti...
Table of contents Object Object Definition Iterat...
This article uses examples to illustrate the usag...
One of the most important features of a style she...
Table of contents 1. Install node 2. Install Comm...
Table of contents 1. Self-enumerable properties 2...
After installing Docker on the Linux server, Pull...
Recently, I encountered a database with the follo...