ES6 では、同期反復の概念が導入されましたが、ES8 では Async オペレーターを参照することで、非同期操作でトラバーサル操作を実行できますか?
今日は、ES9 の非同期トラバーサルの新機能である非同期反復についてお話したいと思います。
非同期トラバーサルを説明する前に、まず ES6 の同期トラバーサルを思い出してください。
ES6 の定義によると、反復は主に 3 つの部分で構成されます:
1. Iterable
まず定義を見てみましょう。 of Iterable:
interface Iterable { [Symbol.iterator]() : Iterator; }
Iterable は、このオブジェクト内に走査可能なデータがあり、Iterator を生成できるファクトリ メソッドを実装する必要があることを意味します。
2. Iterator
interface Iterator { next() : IteratorResult; }
Iterator は Iterable から構築できます。 Iterator はカーソルのような概念であり、IteratorResult には next を通じてアクセスできます。
3. IteratorResult
IteratorResult は、次のメソッドが呼び出されるたびに取得されるデータです。
interface IteratorResult { value: any; done: boolean; }
IteratorResult には、取得するデータを示す値に加えて、トラバーサルが完了したかどうかを示す Done も含まれます。
次は配列を走査する例です:
> const iterable = ['a', 'b']; > const iterator = iterable[Symbol.iterator](); > iterator.next() { value: 'a', done: false } > iterator.next() { value: 'b', done: false } > iterator.next() { value: undefined, done: true }
ただし、上記の例は同期データを走査します。http 側からダウンロードされたファイルなどの非同期データを取得する場合は、次のようにします。ファイルを 1 行ずつ移動します。データ行の読み取りは非同期操作であるため、これには非同期データ トラバーサルが含まれます。
ファイルの非同期読み取りを追加するメソッドは readLinesFromFile であるため、同期トラバーサル メソッドは非同期には適用できなくなりました:
//不再适用 for (const line of readLinesFromFile(fileName)) { console.log(line); }
おそらく、非同期を使用できるのではないかと思われるでしょう。操作をカプセル化してはどうでしょうかPromise 内の行を読み取り、それを同期的に走査するのはどうでしょうか?
アイデアは良いのですが、この場合、非同期操作が完了したかどうかを検出することはできません。したがって、その方法は実現不可能です。
そこで、ES9 では非同期トラバーサルの概念が導入されました。
非同期イテラブル内のイテレータは、Symbol.asyncIterator を通じて取得できます。
非同期イテレータの next() メソッドは、IteratorResults を含む Promises オブジェクトを返します。
それでは、非同期トラバーサルの API 定義を見てみましょう:
interface AsyncIterable { [Symbol.asyncIterator]() : AsyncIterator; } interface AsyncIterator { next() : Promise<IteratorResult>; } interface IteratorResult { value: any; done: boolean; }
非同期トラバーサル アプリケーションを見てみましょう:
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 } });
ここで、createAsyncIterable は A同期反復可能は非同期反復可能に変換されますが、次のセクションでその生成方法を見ていきます。
ここでは主に asyncIterator のトラバーサル操作に焦点を当てます。
Async オペレーターは ES8 で導入されたため、Async 関数を使用して上記のコードを書き直すこともできます。
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 } }
同期反復可能を横断するには for-of を使用し、非同期反復可能を横断するには for-await-of を使用します。
async function f() { for await (const x of createAsyncIterable(['a', 'b'])) { console.log(x); } } // Output: // a // b
await は async 関数内に配置する必要があることに注意してください。
非同期トラバーサル中に例外が発生した場合は、for-await-of で try catch を使用して例外をキャッチできます。
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! } })();
Synchronized iterable returns synchronous iterators, next メソッドは {value を返します、 終わり}。
for-await-of を使用すると、同期反復子は非同期反復子に変換されます。返された値は Promise に変換されます。
同期された next 自体によって返される値が Promise オブジェクトである場合、非同期の戻り値は依然として同じ Promise です。
つまり、次の例に示すように、Iterable<Promise<T>>
を AsyncIterable<T>
に変換します。 #
async function main() { const syncIterable = [ Promise.resolve('a'), Promise.resolve('b'), ]; for await (const x of syncIterable) { console.log(x); } } main(); // Output: // a // b
async function main() { for await (const x of ['a', 'b']) { console.log(x); } } main(); // Output: // c // d
async function* createAsyncIterable(syncIterable) { for (const elem of syncIterable) { yield elem; } }
const asyncGenObj = createAsyncIterable(['a', 'b']); const [{value:v1},{value:v2}] = await Promise.all([ asyncGenObj.next(), asyncGenObj.next() ]); console.log(v1, v2); // a b
async function* prefixLines(asyncIterable) { for await (const line of asyncIterable) { yield '> ' + line; } }
在generator一文中,我们讲到了在generator中调用generator。也就是在一个生产器中通过使用yield*来调用另外一个生成器。
同样的,如果是在异步生成器中,我们可以做同样的事情:
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
如果在异步生成器中抛出异常,这个异常也会被封装在Promise中:
async function* asyncGenerator() { throw new Error('Problem!'); } asyncGenerator().next() .catch(err => console.log(err)); // Error: Problem!
异步方法是使用async function 声明的方法,它会返回一个Promise对象。
function中的return或throw异常会作为返回的Promise中的value。
(async function () { return 'hello'; })() .then(x => console.log(x)); // hello (async function () { throw new Error('Problem!'); })() .catch(x => console.error(x)); // Error: Problem!
异步生成器是使用 async function * 申明的方法。它会返回一个异步的iterable。
通过调用iterable的next方法,将会返回一个Promise。异步生成器中yield 的值会用来填充Promise的值。如果在生成器中抛出了异常,同样会被Promise捕获到。
async function* gen() { yield 'hello'; } const genObj = gen(); genObj.next().then(x => console.log(x)); // { value: 'hello', done: false }
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/es9-async-iteration/
更多编程相关知识,请访问:编程视频!!
以上がES9 の新機能非同期イテレーションの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。