【JavaScript】 非同期イテレータと非同期ジェネレーターについて理解しよう
更新日:2021/01/11
ツイートJavaScriptは反復処理を非同期におこなう非同期イテレータという機能があります。
そこで通常のイテレータとの違いを含めて非同期イテレータについてお伝えします。
また発展形である非同期ジェネレーターについてもお伝えします。
非同期イテレータとは
非同期イテレータは、通常のイテレータと同じようにnextメソッドを持ちます。
ただしnextメソッドが返す値が異なります。
nextが返す値 |
|
---|---|
通常のイテレータ | { value:任意値 , done:真偽値 } |
非同期イテレータ | { value:任意値 , done:真偽値 }で解決するPromiseオブジェクト |
通常のイテレータは、nextメソッドを呼び出すと{ value:任意値 , done:真偽値 }形式のオブジェクトを返します。
doneの値がtrueになるまで繰り返します。
通常のイテレータの例
function myIterator(){ return { count:0, next() { // next:function(){ の短縮系 return this.count < 3 ? { value : this.count++ , done:false} : { done:true }; } } } function func1(){ let iterator = myIterator(); let result = iterator.next(); while( !result.done ){ console.log( result.value ); result = iterator.next(); } } func1();
実行すると、コンソールに1,2,3とログ表示されます。
非同期イテレータは、nextメソッドを呼び出すとPromiseオブジェクトを返します。
Promiseが解決すると{ value:任意値 , done:真偽値 }形式のオブジェクトを返します。
次の例はブラウザ上のボタンが押されるのを待つ非同期イテレータを作成しています。
非同期イテレータの例
window.addEventListener( "DOMContentLoaded" , ()=> { function myAsyncIterator(){ return { count:0, next() { // next:function(){ の短縮系 return new Promise( resolve =>{ if( this.count >= 3 ) resolve( { done: true } ); else{ const count = this.count ++; const btn = document.getElementById("btn"); btn.addEventListener("click",function clickEvent(){ btn.removeEventListener("click",clickEvent); resolve( { value : count , done: false} ); }); } }); } } } async function func2(){ let asyncIterator = myAsyncIterator(); let result = await asyncIterator.next(); while( !result.done ){ console.log( result.value ); result = await asyncIterator.next(); } } func2(); });
HTML
<button id="btn">click</button>
clickを押して動作を確認してみてください。
1,2,3と順番に表示されるはずです。
イテラブルなオブジェクト
上の例のイテレータ関数をオブジェクトに実装して、イテラブルなオブジェクトを作成してみます。
通常のイテラブルなオブジェクトは、[Symbol.iterator]プロパティにイテレータ関数をセットします。
通常のイテラブルなオブジェクト
const obj1 ={ [Symbol.iterator]:myIterator, }; function func1(){ for ( let data of obj1){ console.log( data ); } } func1();
非同期のイテラブルなオブジェクトは、[Symbol.asyncIterator]プロパティに非同期イテレータ関数をセットします。
そして繰り返し処理にfor-await-of構文を使用します。
非同期のイテラブルなオブジェクト
const obj2 ={ data1:"A", data2:"B", data3:"C", [Symbol.asyncIterator]:myAsyncIterator, }; async function func2(){ for await ( let data of obj2){ console.log( data ); } } func2();
func2関数のfor-await-ofは、obj2からPromiseオブジェクトを取得し、Promiseの結果を待ち、受け取ったオブジェクト ({ value:任意値 , done:真偽値 })のvalueプロパティをdataにセットしています。
非同期ジェネレーター
ジェネレーター関数の前にasyncを付加すると、非同期ジェネレーターが生成されます。
async function* 関数名(){ }
通常のジェネレーター関数はyieldキーワードで値を返しますが、非同期ジェネレーター関数はPromiseオブジェクトを返します。
Promiseが解決すると非同期イテレータと同様に{ value:任意値 , done:真偽値 }形式のオブジェクトを返します。
本当にそうなのか確認してみます。
非同期ジェネレーターの動作確認
async function* asyncGenerator(){ yield 1; } function fun1(){ const g = asyncGenerator(); g.next().then( e=>console.log(e) ); // Object { value: 1, done: false } g.next().then( e=>console.log(e) ); // Object { value: undefined, done: true } } fun1();
g.next()がPromiseオブジェクトを返すので、その値に対してthenメソッドを実行することで、Promiseの結果を取得できます。
想定通りの動作をしていることから、非同期ジェネレーターが{ value:任意値 , done:真偽値 }で解決するPromiseを返していることがわかりますね。
上の例は非同期ぽくないので、yield Promiseオブジェクトに変更して、ブラウザ上でボタンを押されるまで待つように修正してみます。
上の例の結果から { value:Promiseオブジェクト , done:真偽値 } が表示されることが予想できます。
ブラウザ上でボタンを押されるまで待つ非同期ジェネレーター
function btnWait(n){ return new Promise( resolve => { const btn = document.getElementById("btn"); btn.addEventListener("click",function clickEvent(){ btn.removeEventListener("click",clickEvent); resolve(n); }); }); } async function* asyncGenerator(){ yield btnWait(1); } function fun1(){ const g = asyncGenerator(); g.next().then( e=>console.log(e) ); g.next().then( e=>console.log(e) ); } fun1();
clickを押してみてください。
次のような結果になるはずです。
{ value: 1, done: false } { value: undefined, done: true }
yieldでPromiseオブジェクトを指定した場合、その結果を待ってくれます。
また、非同期ジェネレーターはそのまま非同期のイテラブルなオブジェクトに適用できます。
非同期ジェネレーターを非同期のイテラブルなオブジェクトに適用
const obj = { [Symbol.asyncIterator]:asyncGenerator, } async function fun1(){ for await( let data of obj){ console.log( data ); // 結果: 1 } } fun1();
記事の内容について

説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
ご意見はこちら。
https://affi-sapo-sv.com/info.php
【お願い】

■このページのURL
■このページのタイトル
■リンクタグ