JavaScript非同期イテレーター

概要: このチュートリアルでは、非同期データを順次アクセスできるJavaScript非同期イテレーターについて学習します。

JavaScript非同期イテレーター入門

ES6では、データを順次アクセスできるイテレーターインターフェースが導入されました。イテレーターは、配列セットマップなどの同期データソースへのアクセスに適しています。

イテレーターインターフェースの主要なメソッドはnext()で、{value, done}オブジェクトを返します。ここで、doneはシーケンスの終わりに達したかどうかを示すブール値であり、valueはシーケンスで生成された値です。

同期データとは、シーケンス内の次のvaluedoneの状態が、next()メソッドが返された時点で既知であることを意味します。

同期データソース以外にも、JavaScriptはI/Oアクセスなどの非同期データソースにアクセスする必要があります。非同期データソースの場合、イテレーターのvaluedoneの状態は、next()メソッドが返された時点では多くの場合不明です。

非同期データソースに対処するために、ES2018では非同期イテレーター(または非同期イテレータ)インターフェースが導入されました。

非同期イテレーターはイテレーターに似ていますが、そのnext()メソッドは、{value, done}オブジェクトに解決されるPromiseを返す点が異なります。

以下は、イテレーターインターフェースを実装するSequenceクラスを示しています。(Sequenceクラスの実装方法の詳細については、イテレーターチュートリアルを参照してください。)

class Sequence {
    constructor(start = 0, end = Infinity, interval = 1) {
            this.start = start;
            this.end = end;
            this.interval = interval;
        }
        [Symbol.iterator]() {
            let counter = 0;
            let nextIndex = this.start;
            return {
                next: () => {
                    if (nextIndex <= this.end) {
                        let result = {
                            value: nextIndex,
                            done: false
                        }
                        nextIndex += this.interval;
                        counter++;
                        return result;
                    }
                    return {
                        value: counter,
                        done: true
                    };
                }
            }
        }
}Code language: JavaScript (javascript)

このSequenceクラスを非同期にするには、次のように変更する必要があります。

  • Symbol.iteratorの代わりにSymbol.asyncIteratorを使用します。
  • next()メソッドからPromiseを返します。

次のコードは、SequenceクラスをAsyncSequenceクラスに変換します。

class AsyncSequence {
    constructor(start = 0, end = Infinity, interval = 1) {
            this.start = start;
            this.end = end;
            this.interval = interval;
        }
        [Symbol.asyncIterator]() {
            let counter = 0;
            let nextIndex = this.start;
            return {
                next: async () => {
                    if (nextIndex <= this.end) {
                        let result = {
                            value: nextIndex,
                            done: false
                        }
                        nextIndex += this.interval;
                        counter++;

                        return new Promise((resolve, reject) => {
                            setTimeout(() => {
                                resolve(result);
                            }, 1000);
                        });
                    }
                    return new Promise((resolve, reject) => {
                        setTimeout(() => {
                            resolve({
                                value: counter,
                                done: true
                            });
                        }, 1000);

                    });
                }
            }
        }
}Code language: JavaScript (javascript)

AsyncSequenceは、1秒ごとにシーケンスの次の数値を返します。

for await...of文

非同期イテラブルオブジェクトを反復処理するには、ES2018でfor await...of文が導入されました。

for await (variable of iterable) {
    // statement
}
Code language: JavaScript (javascript)

awaitキーワードはasync関数でのみ使用できるため、次のようにAsyncSequenceクラスを使用する非同期IIFEを作成できます。

(async () => {

    let seq = new AsyncSequence(1, 10, 1);

    for await (let value of seq) {
        console.log(value);
    }

})();
Code language: JavaScript (javascript)

出力(各数値は1秒ごとに返されます)

1
2
3
4
5
6
7
8
9
10Code language: JavaScript (javascript)

次の表は、イテレーターと非同期イテレーターの違いを示しています。

#イテレーター非同期イテレーター
よく知られたシンボルSymbol.iteratorSymbol.asyncIterator
next()の戻り値は{value, done }{value, done}に解決されるPromise
ループ文for...offor await...of
このチュートリアルは役に立ちましたか?