JavaScriptの非同期処理の歴史

## はじめに

JavaScriptにおいて、非同期処理の扱い・知識は欠かせないものとなっています。
この記事ではそもそも非同期処理とは何なのか、JavaScriptで非同期処理がどのような変遷を遂げてきたのかについて解説していきたいと思います。


### 同期処理

JavaScriptで以下のようにコードを書いた場合、各行の処理が完了するまで次の行の処理が開始されません。
変数`num`が初期化される前に`console.log(num)`が呼び出されることはなく、必ず`num`の初期化が完了してからコンソールに出力されます。

```js
let num = 1;
console.log(num);
num++;
console.log(num);
// => 1
// => 2
```

このように、書いた順番にプログラムが実行され、現在行の処理が完了してから次の行の処理に移るような処理は同期処理と言われています。


#### 同期処理の問題点

JavaScriptにおいて、HTTPリクエストや端末のカメラやマイクへのアクセス、ファイルの読み込みといった完了までに長い時間がかかる可能性のある機能を使用する際、
それらの機能が完了するまで処理が止まってしまうとその他の処理や画面操作（リンクの押下やテキストボックスへの入力等）も受け付けなくなります
（厳密には実行待ちの状態になり、「長い時間のかかる処理」が完了するまで実行されず、画面がフリーズしたように見える）。
この状態については、MDNの[長時間実行される同期関数の問題点](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Asynchronous/Introducing#%E9%95%B7%E6%99%82%E9%96%93%E5%AE%9F%E8%A1%8C%E3%81%95%E3%82%8C%E3%82%8B%E5%90%8C%E6%9C%9F%E9%96%A2%E6%95%B0%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9)にわかりやすい例が載っています。


### 非同期処理

前述のような完了までに長い時間を要する処理を行う際、処理が完了するのを待たずに次の処理を実行する仕組みが非同期処理です。
例えばGoogleMapのようなWebサービスが非同期処理を行わず、すべて同期処理で実装されていた場合、裏でサーバーと通信し、画面を更新するような処理が走るたびに画面が一切の操作を受け付けずに処理の完了を待つ時間が生じる、非常に使いづらいサービスとなってしまいます。
そのため、定期的にサーバーと通信するようなWebアプリケーション制作では非同期処理は欠かせない技術となっています。

以下MDNより引用

> 非同期プログラミングは、長く続く可能性のあるタスクを開始しても、そのタスクが完了するまで待つのではなく、そのタスクの実行中も他のイベントに応答できるようにする技術です。タスクが完了すると、プログラムはその結果を表示します。


## JavaScriptにおける非同期処理の歴史

### ES2015以前
ES2015以前は非同期処理を行いたい場合、callback関数とイベントハンドラーを用いるのが一般的でした。

#### callback関数

callback関数はいわゆる普通の関数ですが、ほかの関数へ引数として渡され、その関数内で適切なタイミングで実行されるものです。
callback関数を受け取る関数としては、下記のような`setTimeout`をはじめとして様々なものがあります。

```js
// 処理開始時の時刻を出力
console.log(new Date());

// 5000ミリ秒経過したら第一引数に渡したcallback関数を実行する
setTimeout(function () {
    // 5000ミリ秒後の時刻を出力
    console.log(new Date());
}, 1000 * 5);
```

#### イベントハンドラー

イベントハンドラーとは、画面をクリックする・画面の読み込みが完了する、といったイベントが発生した際にイベントに紐づいた処理（関数）を実行する機能です。
ボタンをクリックすると音が鳴る、画面をスクロールすると画像が切り替わるといったユーザの操作に合わせて、紐づいた処理を行ってくれます。

```js
/** 画面をクリックするとクリックした箇所の座標を表示する処理 */

// addEventListenerにイベント名とcallback関数を渡す方法
window.addEventListener("click", function(event) {
    alert(`座標${event.offsetX}:${event.offsetY}がクリックされました！`);
});

// on + イベント名に関数を代入する方法
window.onclick = function(event) {
    alert(`座標${event.offsetX}:${event.offsetY}がクリックされました！`);
}
```

#### Ajax(Asynchronous JavaScript and XML)

ページ全体を再読み込みせずにサーバーと情報をやり取りして画面を動的に更新する、といった非同期処理をJavaScriptを用いて行いたい場合はAjaxと呼ばれる手法を用いることが主流でした。
古くは2004年に発表されたGMailでも用いられており、Webアプリケーションの時代の幕開けに大きく貢献した手法です。

そのコアとなるのがHTTPリクエストを送信する`XMLHttpRequest`オブジェクトです。
`XMLHttpRequest`には`load`や`error`、`loadend`といったイベントハンドラーが存在し、それらのイベントリスナーとcallback関数を用いて非同期処理の実装を行っています。

```js
// XMLHttpRequestオブジェクトを生成
var xhr = new XMLHttpRequest();

// メソッドとURLを指定してリクエスト内容を設定
xhr.open('GET', 'https://qiita.com/api/v2/users/BAITO0123/items?page=1');
xhr.responseType = "json";

// 正常に完了した時のイベントを登録
xhr.addEventListener('load', function () {
    console.log(xhr.response);
});

// エラー発生時のイベントを登録
xhr.addEventListener('error', function () {
    console.log(xhr.err);
});

// 正常・エラー関係なくリクエスト完了した時のイベントを登録
xhr.addEventListener('loadend', function () {
    // 正常時の処理
    if(xhr.status === 200) {
        console.log(xhr.response);
    }
    // エラー時の処理
    else {
        console.log(xhr.response);
    }
});

// リクエストを送信
xhr.send();
```

#### callback地獄
callback関数を用いた非同期処理ではとある非同期処理が完了してからまた別の非同期処理を行いたい場合、callback関数の内部で非同期関数を実行し、またそれにcallback関数を渡しその内部でまた非同期関数を実行してcallback関数を渡す・・・といったcallback地獄へと誘われる危険性があります。

callback地獄はやたらと内部のネストが深くなり、エラーハンドリングも各スコープ内で行う必要があるなど、非常に面倒かつ可読性の問題もありました。

```js
var xhr1 = new XMLHttpRequest();

xhr1.open('GET', 'https://qiita.com/api/v2/users/BAITO0123/items?page=1');
xhr1.responseType = "json";

xhr1.addEventListener('loadend', function () {
    if (xhr1.status === 200) {
        console.log(xhr1.response);

        // xhr1の処理が正常に完了したらxhr2の処理を開始する
        var xhr2 = new XMLHttpRequest();
        xhr2.open('GET', 'https://qiita.com/api/v2/users/BAITO0123/items?page=1');
        xhr2.responseType = "json";
    
        xhr2.addEventListener('loadend', function() {
            if (xhr2.status === 200) {
                console.log(xhr2.response);
                // xhr2の処理が正常に完了したらxhr3の処理を開始する
                var xhr3 = new XMLHttpRequest();
                
                xhr3.open('GET', 'https://qiita.com/api/v2/users/BAITO0123/items?page=1');
                xhr3.responseType = "json";
            
                xhr3.addEventListener('loadend', function() {
                    if (xhr3.status === 200) {
                        console.log(xhr3.response);
                    }
                    else {
                        // エラーが帰ってきたら内容を出力する
                        console.log(xhr3.status + " " + xhr3.response);
                    }
                });
                xhr3.send();
            }
            else {
                // エラーが帰ってきたら内容を出力して終了する
                console.log(xhr2.status + " " + xhr2.response);
            }
        });
        xhr2.send();
    }
    else {
        // エラーが帰ってきたら内容を出力して終了する
        console.log(xhr1.status + " " + xhr1.response);
    }
});

xhr1.send();
```

上記ファイルのAjax処理部分を関数に分けて呼び出すと以下のようになります。
```js
// URLとcallback関数を受け取り、処理が正常に完了したらcallback関数を実行する
function ajax(url, callback) {
    var xhr = new XMLHttpRequest();

    xhr.open('GET', url);
    xhr.responseType = "json";
    
    xhr.addEventListener('loadend', function () {
        if (xhr.status === 200) {
            console.log(xhr.response);
            if (callback) {
                callback();
            }
        }
        else {
            console.log(xhr.status + " " + xhr.response);
        }
    });
    xhr.send();
}

// 呼び出し側は多少スッキリするがcallback地獄は変わらず・・・
ajax("https://qiita.com/api/v2/users/BAITO0123/items?page=1", function() {
    ajax("https://qiita.com/api/v2/users/BAITO0123/items?page=1", function() {
        ajax("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    });
});
```

#### Generator関数
上記のようなcallback地獄に対抗するためにGenerator関数を用いた手法がとられていたこともあります。
Generator関数はざっくり言うと`yield`演算子で処理が止まり、`next()`関数を呼び出すことで処理が再開されるという特性を持った関数です。
`yield`で処理を止め、呼び出し先の非同期処理を行う関数内で`next()`関数を実行することで同期処理的な記述で非同期処理を実行することができます。

```js
// GETメソッドを送信して取得結果を返すメソッド
function requestGetMethod(gen, url) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = "json";
    
    xhr.addEventListener('loadend', function () {
        // 処理が完了したら値を返して次の処理を実行する
        gen.next(xhr.response);
    });
    xhr.send();
}

// Generator関数を作成してnext()を呼び出す
var generator = (function *() {
    // requestGetMethod内でnext()関数が実行されるまで処理を停止し、next()関数の引数をresult1に代入する
    var result1 = yield requestGetMethod(generator, "https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    console.log(result1);
    var result2 = yield requestGetMethod(generator, "https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    console.log(result2);
    var result3 = yield requestGetMethod(generator, "https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    console.log(result3);
})();

generator.next();
```

### ES2015以降

#### Promise

ES2015では上記のcallback地獄を解消する`Promise`オブジェクトが追加されました。
Promiseオブジェクトは非同期処理の状態、およびその結果を保持しており、非同期処理が正常に完了した場合、失敗した場合に処理を結び付けて実行できます。

##### Promiseの状態

Promiseオブジェクトは以下の3つのいずれかの状態を持っています。
* pending
Promiseオブジェクトが作成された初期の状態でまだ処理が完了も失敗もしていない状態。

* fulfilled
処理が正常に完了した状態。Promiseオブジェクト内で`resolve()`が呼び出されるとこの状態に移行し、`resolve()`の引数を値として返す。

* rejected
処理が失敗した状態。Promiseオブジェクト内で`reject()`が呼び出されるとこの状態に移行し、`reject()`の引数をエラー内容として返す。

##### then/catch

Promiseオブジェクトは`then()`関数を呼び出すことでそれぞれ`fulfilled`/`rejected`に移行したタイミングで引数に渡した処理を実行できます。

`then`関数は引数としてonFulfilled、onRejectedの二つの関数を取り、`fulfilled`時には第一引数に渡した関数、`rejected`時には第二引数に渡した関数が実行され、それらの戻り値をPromiseでラップして返します。
第二引数を省略した場合は自動的に`rejected`された値をエラーとしてスローしてくれます。そのため、どこかの`then`関数内でエラーが発生した場合は後述する`catch`関数内でまとめてエラーハンドリングを行うことができます。

`catch`関数は引数として`onRejected`関数を取り、`rejected`時に関数を実行し、その戻り値をPromiseでラップして返します。

:::note info
`catch`関数は`then(undefined, err => {...});`の省略系として扱われます。
:::

```js
// fulfilled状態のPromiseオブジェクトを生成
const p1 = Promise.resolve("resolved!");

p1.then(val => {
    console.log(val);
}, err => {
    console.error("error " + err);
});
// => "resolved!"

// rejected状態のPromiseオブジェクトを生成
const p2 = Promise.reject("rejected!");
// 自動的に第二引数が置き換えられ、 err => { throw err }の処理に入る
p2.then(val => {
    console.log(val);
    return "resolved!";
})
// ここも自動的に第二引数が置き換えられ、 err => { throw err; }の処理に入る
.then(val => {
    console.log(val);
})
.catch(err => {
    console.error("error " + err);
});
// => "error rejected!"

const p3 = Promise.reject("rejected!");
// 第二引数の処理に入り、fulfilledなPromiseを返す。
p3.then(val => {
    console.log(val);
}, err => {
    // 例外処理をして、次のthenのonFulfilledの処理に入る
    console.log("onRejected " + err);
    return "fulfilled";
})
.then(val => {
    console.log(val);
})
.catch(err => {
    console.error("error " + err);
});
// => "onRejected rejected!"
// => "fulfilled"
```

#### さらばcallback地獄

先ほどのAjax通信をする処理をPromiseを用いて実装すると以下のようになります。

:::note warn
ES2015では非同期通信をより簡単に行える`fetch`関数が追加され、現在はそちらが主流となっていますが、比較のため敢えてAjaxを使って実装しています。
:::

```js
// Ajaxの処理結果をPromiseでラップして返す関数
const ajaxPromise = (url) => {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);

        xhr.responseType = "json";
        xhr.addEventListener("loadend", () => {
            if (xhr.status === 200) {
                // 正常に完了したらresolve()で値を返し、fulfilledに移行させる
                resolve(xhr.response);
            }
            else {
                // エラーが発生したらreject()でrejectedに移行させる
                reject(xhr.response);
            }
        });
        xhr.send();
    })
}

// .then()チェーンで順次実行する
ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1")
.then(res => {
    console.log(res);
    return ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
})
.then(res => {
    console.log(res);
    return ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
})
.then(res => {
    console.log(res);
})
// エラーハンドリングはまとめて行う
.catch(err => {
    console.log(err);
});
```

#### （備考）fetchの場合
`fetch`を用いて実装した場合は以下のようになります。
`fetch`関数はPromiseでラップされた値を返すため、そのまま`then()`でつなぐことが可能です。

```js
fetch("https://qiita.com/api/v2/users/BAITO0123/items?page=1")
.then(res => res.json())
.then(json => {
    console.log(json);
    return fetch("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
})
.then(res => res.json())
.then(json => {
    console.log(json);
    return fetch("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
})
.then(res => res.json())
.then(json => {
    console.log(json);
})
.catch(err => {
    console.log(err);
});

```

#### Promiseの問題
Promiseの登場によってcallback地獄からの脱却は叶いました。
がしかし、以下のような課題もあります。

* ひたすら`then()`と`catch()`でつなぐのが大変
* 取得結果を`then()`スコープ外の変数に代入しないと前の結果を参照できない

### ES2017以降

#### async/awaitの登場

前述したようなPromiseの弱点を解消するべくES2017にて登場したのが`async/await`です。
これらはPromiseの糖衣構文であり、Promiseを用いた非同期処理をより簡潔に記述できるようにしたものです。

`async function() {}`のように、`function() {}`宣言に`async`キーワードを付けることで、自動的にPromiseでラップされた値を返す関数を宣言することができます。

async function内で`await`キーワードを用いることで、Promiseオブジェクトを返すような非同期関数の処理が完了するのを待ち、同期的に処理を記述することが可能となります。

:::note info
ES2022からはasync function外でも`await`を用いた非同期関数呼び出しが可能となりました。（トップレベルawait）
:::

先ほど定義した`ajaxPromise()`関数を`await`を用いて同期的な処理のように記述した結果が以下です。

```js
const ajaxPromise = (url) => {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);

        xhr.responseType = "json";
        xhr.addEventListener("loadend", () => {
            if (xhr.status === 200) {
                resolve(xhr.response);
            }
            else {
                reject(xhr.response);
            }
        });
        xhr.send();
    })
}

// ES2021までの呼び出し方
(async () => {
    try {
        // ajaxPromise()で取得したPromiseがfulfilledに移行するまで以降の処理が行われない
        const result1 = await ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
        console.log(result1);
        const result2 = await ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
        console.log(result2);
        // => 常にresult1 → result2の順番で結果が出力される
    }
    catch (e) {
        console.log(e);
    }
})();

// ES2022以降の呼び出し方
try {
    const result1 = await ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    console.log(result1);
    const result2 = await ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
    console.log(result2);
}
catch (e) {
    console.log(e);
}

// fetchを使った場合
try {
    const result1 = await (await fetch("https://qiita.com/api/v2/users/BAITO0123/items?page=1")).json();
    console.log(result1);
    const result2 = await (await fetch("https://qiita.com/api/v2/users/BAITO0123/items?page=1")).json();
    console.log(result2);
}
catch (e) {
    console.log(e);
}
```

`then`や`catch`を用いたメソッドチェーンで繋げずに記述できるようになり、かなりスッキリと直感的に分かりやすい記述になっています。
また、取得結果を変数に格納するのも容易になっているほか、`try...catch`を用いた通常のエラーハンドリングも可能になっています。

`await`を用いることで、先ほど挙げた問題点が解消されているのがわかりますね。

### そして応用へ・・・

#### Promise.all 

引数に渡したPromiseオブジェクトの配列がすべて`fulfilled`状態になった時に`then`の処理を実行します。
逆にPromise配列のうちどれか一つでも`rejected`状態になったら`catch`の処理に移ります。

```js
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.all([result1, result2, result3]).then(results => {
    // どの順番で処理が完了しようとも、必ず引数の順番（result1 → result2 → result3の順）で処理が行われる
    for (const result of results) {
        console.log(result);
    }
}).catch(err => {
    console.log(err);
});

// 一つでもrejectされたらthenには入らない
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = Promise.reject("rejected");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.all([result1, result2, result3]).then(results => {
    for (const result of results) {
        console.log(result);
    }
}).catch(err => {
    console.log(err);
});

// Promise.all自体をawaitで待つ
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

try {
    const results = await Promise.all([result1, result2, result3]);
    for (const result of results) {
        console.log(result);
    }
}
catch (e) {
    console.log(e);
}
```

:::note warn
Promise.all()にはPromiseの配列を渡す必要があるため、`await`キーワードを引数に渡すと並列実行されません。
:::

```js
// result1はPromiseオブジェクトではないため、並列に実行されない（出力自体は正しく行われる）
const result1 = await ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

// result1の完了を待ってからPromiseオブジェクトが生成されてしまう
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.all([result1, result2, result3]).then(results => {
    for (const result of results) {
        console.log(result);
    }
}).catch(err => {
    console.log(err);
});

// 引数に文字列を渡しても一応出力される
Promise.all([result1, result2, result3, "Promiseではありません。"]).then(results => {
    for (const result of results) {
        console.log(result);
    }
    // => result1, result2, result3, "Promiseではありません。"が出力される
}).catch(err => {
    console.log(err);
});
```

#### Promise.allSettled

ES2020で追加された関数で、引数に渡したPromiseオブジェクトがすべて完了（成否問わず）したタイミングで`onFulFilled`の処理に入ります。
`Promise.all`とは異なり、`rejected`されたPromiseが存在したり、すべての結果が`rejected`だったとしても`onFulFilled`の処理に入ることができるという特徴があります。

```js
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = Promise.reject("rejected!");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.allSettled([result1, result2, result3]).then(results => {
    for (const result of results) {
        console.log(result);
        // => { status: "fulfilled", value: [{...}]}
        // => { status: "rejected", reason: "rejected!"}
        // => { status: "fulfilled", value: [{...}]}
    }
});
```

#### Promise.any

ES2021で追加された関数で、引数に渡したPromise配列のうちいずれか一つが`fulfilled`状態になったタイミングで`onFulFilled`の処理に移ります。

全てのPromiseが`rejected`状態になったときは`catch`処理に移ります。

```js
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.any([result1, result2, result3]).then(result => {
    // result1, result2, result3のうち、一番最初にfulfilledとなったいずれか一つの結果をコンソールに表示する
    console.log(result);
});

// awaitも併用可能
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

const result = await Promise.any([result1, result2, result3]);
```

#### Promise.race

引数に渡したPromise配列のうちいずれか一つが完了したタイミングで`onFulFilled`または`catch`の処理に移ります。
`Promise.any`と異なり、`fulfilled`、`rejected`どちらの状態になったとしても処理に移ります。

```js
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

Promise.race([result1, result2, result3]).then(result => {
    // result1, result2, result3のうち、一番最初にfulfilled or rejectedとなったいずれか一つの結果をコンソールに表示する
    console.log(result);
});

// awaitも併用可能
const result1 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result2 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
const result3 = ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");

const result = await Promise.race([result1, result2, result3]);
```

#### for await of ...

ES2018で追加された機能で非同期な反復可能オブジェクトに対して非同期に`for of ...`ループを実行することが可能となります。

非同期な反復可能オブジェクトは前述したGenerator関数に`async`を付けることで生成できます。
1件ずつ順番に非同期処理をループしたい場合等に用いることができますが、逆に処理の実行順序が重要ではない場合には`Promise.all`や`Promise.allSettled`を使用する方がよいかもしれません。

正直この機能を使うのに向いている処理が思いつかないので使用例があればぜひコメントで教えてください。

```js
// async function* を用いて生成した非同期な反復可能オブジェクト
const asyncIterator = (
    async function* () {
        try {
            yield ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
            yield ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
            yield ajaxPromise("https://qiita.com/api/v2/users/BAITO0123/items?page=1");
        }
        catch (e) {
            console.log(e);
        }
    }
)();

// val = await asyncIterator.next(); のようなイメージ
for await (const val of asyncIterator) {
    console.log(val);
}
```

## 結論

* 2023年現在、JavaScriptで非同期処理を行う場合は`async/await`が主流
* 簡単かつ同期的に処理を記述でき、非同期処理同士の実行順序をコントロールできてとても便利！
* 加えて`Promise`についても理解しておくと尚良し！

## おわりに

さて、JavaScriptにおける非同期処理の歴史をひたすらにまとめてきましたがいかがでしたでしょうか？
JavaScriptの非同期処理は長きにわたって変化を遂げてきました。Ajaxが主流だったころに比べて現在のasync/awaitはかなり記述量が少なく、簡単かつ直感的に分かりやすくなっています。

ひたすらに書く機会の多いJavaScriptの非同期処理がここまで手軽に書けるようになったことに感謝ですね。
皆さんもasync/awaitを使うたびに感謝の気持ちを胸に刻みましょう。

それでは今回はこの辺で失礼します。最後まで読んでいただきありがとうございました！
よかったらいいねボタンやチャンネル登録（フォロー）をよろしくお願いします！！（Youtuber風）

## 参考
https://www.jetbrains.com/ja-jp/lp/javascript-25/

https://qiita.com/yunity29/items/7ccc84d47e139340ecbc

https://www.messiahworks.com/archives/21598

https://butterbull.hatenablog.com/entry/2019/08/07/000000

https://qiita.com/xx2xyyy/items/1a4b9541b7e9ee943f06

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Generator

https://developer.mozilla.org/ja/docs/Web/Guide/AJAX

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function