ES9の新機能の詳細な説明: 非同期反復

ES9の新機能の詳細な説明: 非同期反復

非同期トラバーサル

非同期トラバーサルを説明する前に、ES6 の同期トラバーサルを思い出してみましょう。

ES6 の定義によると、反復は主に 3 つの部分で構成されます。

反復可能

まず、Iterable の定義を見てみましょう。

インターフェース Iterable {
    [Symbol.iterator]() : イテレータ;
}

Iterable は、このオブジェクトにトラバース可能なデータが含まれており、Iterator を生成できるファクトリ メソッドを実装する必要があることを意味します。

イテレータ

インターフェースイテレータ{
    次の(): イテレータの結果;
}

イテレータは Iterable から構築できます。 Iterator はカーソルに似た概念であり、IteratorResult には next を通じてアクセスできます。

イテレータ結果

IteratorResult は、次のメソッドが呼び出されるたびに取得されるデータです。

インターフェースIteratorResult {
    値: 任意;
    完了: ブール値;
}

取得するデータを表す値に加えて、IteratorResult には、トラバーサルが完了したかどうかを示す done もあります。

以下は配列を反復処理する例です。

> const iterable = ['a', 'b'];

> const iterator = iterable[Symbol.iterator]();

イテレータ.next()

{ 値: 'a'、完了: false }

イテレータ.next()

{ 値: 'b'、完了: false }

イテレータ.next()

{ 値: 未定義、完了: true }

ただし、上記の例では同期データを走査しています。http 側からダウンロードしたファイルなどの非同期データを取得する場合は、ファイルを 1 行ずつ走査する必要があります。データ行の読み取りは非同期操作であるため、非同期データの走査が必要になります。

非同期ファイル読み取りメソッド readLinesFromFile を追加すると、同期トラバーサル メソッドは非同期には適用されなくなります。

// (const line of readLinesFromFile(fileName)) には適用されなくなりました {
    console.log(行);
}

非同期的に 1 行を読み取り、それを同期的に走査する操作を Promise にカプセル化できるのだろうかと疑問に思うかもしれません。

アイデアは良いのですが、この場合、非同期操作が完了したかどうかを検出することは不可能です。したがって、この方法は実行可能ではありません。

そこで ES9 では非同期トラバーサルの概念が導入されました。

1. 非同期反復可能オブジェクトの反復子を取得するには、Symbol.asyncIterator を使用できます。

2. 非同期イテレータの next() メソッドは、IteratorResults を含む Promises オブジェクトを返します。

それでは、非同期トラバーサルの API 定義を見てみましょう。

インターフェースAsyncIterable {
    [Symbol.asyncIterator]() : AsyncIterator;
}
インターフェースAsyncIterator {
    次のメソッド: Promise<IteratorResult>;
}
インターフェースIteratorResult {
    値: 任意;
    完了: ブール値;
}

非同期トラバーサル アプリケーションを見てみましょう。

定数 asyncIterable = createAsyncIterable(['a', 'b']);
asyncIterator は、 .asyncIterable のインスタンスを生成する。
asyncIterator.next()
.then(iterResult1 => {
    console.log(iterResult1); // { 値: 'a'、完了: false }
    asyncIterator.next() を返します。
})
.then(iterResult2 => {
    console.log(iterResult2); // { 値: 'b'、完了: false }
    asyncIterator.next() を返します。
})
.then(iterResult3 => {
    console.log(iterResult3); // { 値: undefined、完了: true }
});

その中で、createAsyncIterable は同期反復可能オブジェクトを非同期反復可能オブジェクトに変換します。次のセクションで、これがどのように生成されるかを見ていきます。

ここでは主に asyncIterator のトラバーサル操作に焦点を当てます。

ES8 では Async 演算子が導入されたため、上記のコードを Async 関数を使用して書き直すこともできます。

非同期関数f(){
    定数 asyncIterable = createAsyncIterable(['a', 'b']);
    asyncIterator は、 .asyncIterable のインスタンスを生成する。
    console.log(asyncIterator.next() を待機します);
        // { 値: 'a'、完了: false }
    console.log(asyncIterator.next() を待機します);
        // { 値: 'b'、完了: false }
    console.log(asyncIterator.next() を待機します);
        // { 値: 未定義、完了: true }
}

非同期反復可能トラバーサル

同期反復可能オブジェクトを走査するには for-of を使用し、非同期反復可能オブジェクトを走査するには for-await-of を使用します。

非同期関数f(){
    await (const x of createAsyncIterable(['a', 'b'])) {
        コンソールログ(x);
    }
}
// 出力:
// は
// ば

await は async 関数内に配置する必要があることに注意してください。

非同期トラバーサルで例外が発生した場合は、for-await-of で try catch を使用して例外をキャッチできます。

関数createRejectingIterable() {
    戻る {
        [シンボル.asyncIterator]() {
            これを返します。
        },
        次() {
            Promise.reject(new Error('Problem!')) を返します。
        },
    };
}
(非同期関数() { 
    試す {
        await (const x of createRejectingIterable()) {
            コンソールログ(x);
        }
    } キャッチ (e) {
        コンソールエラー(e);
            // エラー: 問題!
    }
})();

同期イテラブルは同期イテレータを返し、次のメソッドは {value, done} を返します。

for-await-of を使用すると、同期イテレータは非同期イテレータに変換されます。返された値は Promise に変換されます。

同期 next 自体によって返される値が Promise オブジェクトである場合、非同期の戻り値は同じ Promise のままです。

つまり、次の例に示すように、Iterable<Promise<T>> を AsyncIterable<T> に変換します。

非同期関数main() {
    const syncIterable = [
        Promise.resolve('a')、
        Promise.resolve('b')、
    ];
    await (const x of syncIterable) {
        コンソールログ(x);
    }
}
主要();

// 出力:
// は
// ば

上記の例では、同期 Promise を非同期 Promise に変換します。

非同期関数main() {
    待機(const x of ['a', 'b']) {
        コンソールログ(x);
    }
}
主要();

// 出力:
// は
// d

上記の例では、同期定数を Promise に変換します。 結果は同じであることがわかります。

非同期反復生成

上記の例に戻ると、createAsyncIterable(syncIterable) を使用して syncIterable を AsyncIterable に変換します。

このメソッドがどのように実装されるか見てみましょう:

非同期関数* createAsyncIterable(syncIterable) {
    for (const syncIterable の要素) {
        要素を生成します。
    }
}

上記のコードでは、通常のジェネレーター関数の前に async を追加しています。これは、非同期ジェネレーターであることを意味します。

通常のジェネレーターの場合、 next メソッドが呼び出されるたびに、オブジェクト {value, done} が返されます。このオブジェクトは、yield 値をカプセル化したものです。

非同期ジェネレーターの場合、 next メソッドが呼び出されるたびに、オブジェクト {value, done} を含む promise オブジェクトが返されます。このオブジェクトは、yield 値をカプセル化したものです。

Promise オブジェクトが返されるため、次のメソッドを再度呼び出す前に、非同期実行の結果が完了するのを待つ必要はありません。

Promise.all を使用すると、すべての非同期 Promise 操作を同時に実行できます。

asyncGenObj は、createAsyncIterable を呼び出します。
const [{値:v1},{値:v2}] = await Promise.all([
    asyncGenObj.next()、asyncGenObj.next()
]);
console.log(v1, v2); // アブ

createAsyncIterable では、同期 Iterable から非同期 Iterable を作成します。

次に、非同期 Iterable から非同期 Iterable を作成する方法を見てみましょう。

前のセクションでは、for-await-of を使用して非同期 Iterable からデータを読み取ることができることがわかったので、次のように使用できます。

非同期関数* prefixLines(asyncIterable) {
    for await (asyncIterableのconst行) {
        '> ' + 行を生成します。
    }
}

ジェネレータの記事では、ジェネレータ内でジェネレータを呼び出すことについて説明しました。つまり、プロデューサーでは、yield* を使用して別のジェネレーターが呼び出されます。

同様に、非同期ジェネレータでも同じことを行うことができます。

非同期関数* gen1() {
    'a' を生成します。
    'b' を生成します。
    2を返します。
}
非同期関数* gen2() {
    const 結果 = yield* gen1(); 
        // 結果 === 2
}

(非同期関数() {
    gen2() の const x を待機します。
        コンソールログ(x);
    }
})();
// 出力:
// は
// ば

非同期ジェネレーターで例外がスローされた場合、その例外も Promise にラップされます。

非同期関数* asyncGenerator() {
    新しいエラーをスローします('問題があります!');
}
asyncGenerator().next()
.catch(err => console.log(err)); // エラー: 問題あり!

非同期メソッドと非同期ジェネレーター

非同期メソッドは、 async 関数を使用して宣言され、 Promise オブジェクトを返すメソッドです。

関数内で返される例外またはスローされる例外は、返される Promise の値として使用されます。

(非同期関数() {
    'hello' を返します。
})()
.then(x => console.log(x)); // こんにちは

(非同期関数() {
    新しいエラーをスローします('問題があります!');
})()
.catch(x => console.error(x)); // エラー: 問題あり!

非同期ジェネレーターは、async 関数 * を使用して宣言されたメソッドです。非同期反復可能オブジェクトを返します。

iterable の next メソッドを呼び出すと、Promise が返されます。非同期ジェネレータによって生成された値は、Promise の値を埋めるために使用されます。ジェネレーターで例外がスローされた場合、それは Promise によってもキャッチされます。

非同期関数* gen() {
    'hello' を返します。
}
定数 genObj = gen();
genObj.next().then(x => console.log(x));
    // { 値: 'hello'、完了: false }

以上がES9の新機能である非同期反復の詳細な説明です。ES9の新機能である非同期反復の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • ES6 イテレータトラバーサルの原則、応用シナリオ、関連する共通知識の拡張の詳細な説明
  • ES6 の新機能 2: イテレータ (トラバーサル) と for-of ループの詳細な説明
  • ES6 ディレクトリトラバーサル関数の例
  • ES6 における Iterator と for..of.. トラバーサルの使用法の分析
  • ES9の新機能である正規表現RegExpの詳しい説明
  • ES7 の JavaScript 修飾子を理解する
  • ES7 における Async/await の使用に関する詳細な説明
  • ES7 で Await を使用してコールバックのネストを減らす方法の詳細な説明

<<:  CentOS 7.x のマスターおよびスレーブ DNS サーバーの展開

>>:  Mysqlデータベースの文字化けに対処する方法

推薦する

MySQL オンライン DDL ツール gh-ost 原理分析

目次1. はじめに1.1 原則1.2 プロセス1.3 特徴1.4 githubアドレス2. テスト環...

ウェブページ作成時に標準 HTML コードを使用する際のポイント

<br />多くのウェブサイト デザイナーが犯す最も一般的な間違いは、ウェブページが I...

単一のdivの正多角形変換を実現する純粋なCSS

前回の記事では、beforeとafterの擬似要素を使用してMaterial Designスタイルの...

Vue はデータの変更をどのように追跡しますか?

目次背景例誤解 - コールスタックを表示するためにウォッチでブレークポイントを設定する正しいアプロー...

SQL 文を使用してデータを収集する場合の sum 関数と count 関数の if 判定条件の使用法の説明

まず、例を挙げてみましょう(読みたくない場合は、以下の要約だけ読んでください)。 order_typ...

JSの基本概念の詳細な紹介

目次1. JSの特徴1.1 マルチパラダイム1.2 説明1.3 シングルスレッド1.4 ノンブロッキ...

MySQL でストアド プロシージャを作成し、ループでレコードを追加する方法

この記事では、例を使用して、MySQL でストアド プロシージャを作成し、ループでレコードを追加する...

ソースコードの観点からキープアライブコンポーネントのキャッシュ原理に答える

今日は、早速本題に入り、面接中に尋ねられた質問、つまりキープアライブ コンポーネントのキャッシュ原理...

CSS を使用して三角形を実装する一般的な手法 (複数の方法)

面接の経験によっては、CSS に関する質問がよく見られ、CSS を使用して三角形を描画する方法につい...

Linux/CentOS サーバー セキュリティ構成の一般ガイド

Linux はオープン システムです。インターネット上には、既成のプログラムやツールが多数存在します...

Linux zabbix エージェントの展開と設定方法の詳細な説明

1. web01にzabbix-agentをインストールするZabbix ウェアハウスをデプロイする...

MySQL 8.0.20でNavicatをインストールして接続する方法と注意すべき点

注意事項1. まず、mysql インストール ディレクトリに次の内容の my.ini ファイルを作成...

サイト全体を灰色にするCSSコードのまとめ

国務院は本日、新型コロナウイルス感染症との闘いで殉教した方々と犠牲者に対し、全国各民族人民の深い哀悼...

LinuxシステムでFuserコマンドを使用する方法

Fuser コマンドとは何ですか? fuser コマンドは、特定のファイル、ディレクトリ、またはソケ...

CentOS7 環境での DHCP 設定チュートリアル

目次CentOS7環境での設定コマンド手順1. DHCP設定ファイルを設定する2. グローバル構成を...