JavaScriptの非同期処理: Callback HellからPromiseとasync/awaitへの移行

こんにちは！JavaScriptの非同期処理について深掘りし、その解決法について詳しく説明するこの記事をお楽しみいただければ幸いです。この記事は主に「Callback Hell」の問題をどのように解決するか、そしてPromiseとasync/awaitがその役割をどのように果たすかに焦点を当てています。

## Callback Hellとは？

JavaScriptは非同期言語であるため、処理がすぐに完了しない場合でもプログラムが停止することなく進行します。これは、ネットワークリクエストやデータベース操作など、時間がかかる操作を行う場合に特に便利です。

しかし、非同期処理はコールバック関数を使うことが一般的で、これが多くなるとコードが複雑になり、読みにくくなる問題が生じます。これを「Callback Hell（コールバック地獄）」と呼びます。以下にその例を示します：

```javascript
let horoscope = {
    signs: ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"],
    fortunes: ["Excellent luck today", "Good luck today", "Average luck today", "Bad luck today", "Terrible luck today"]
};

let requestSign = (signIndex, call_prediction) => {
    setTimeout(() => {
        console.log(`${horoscope.signs[signIndex]} was selected`);
        call_prediction();
    },2000);
};

let prediction = () => {
    setTimeout(() =>{
        console.log("Prediction has started");
        setTimeout(() => {
            console.log("Fetching data");
            setTimeout(() => {
                console.log(`Your fortune is: ${horoscope.fortunes[Math.floor(Math.random() * horoscope.fortunes.length)]}`);
            }, 2000);
        },2000);
    }, 0);
};

requestSign(Math.floor(Math.random() * horoscope.signs.length), prediction);
```

このコードは「星座に基づいた今日の運勢」をテーマに非同期処理を行っています。星座をランダムに選択し、その後データをフェッチし、最終的に運勢を表示しています。しかし、このコードは読みにくく、保守しにくいという問題があります。

## Promiseとasync/awaitの導入

そこで登場するのがPromiseとasync/awaitです。これらはJavaScriptの非同期処理をより簡単に、そして読みやすく書くためのパターンです。

以下にPromiseとasync/awaitを用いて書き直したコードを示します：

```javascript
let hor

oscope = {
    signs: ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"],
    fortunes: ["Excellent luck today", "Good luck today", "Average luck today", "Bad luck today", "Terrible luck today"]
};

let requestSign = (signIndex) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`${horoscope.signs[signIndex]} was selected`);
            resolve();
        }, 2000);
    });
};

let fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("Fetching data");
            resolve();
        }, 2000);
    });
};

let showFortune = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`Your fortune is: ${horoscope.fortunes[Math.floor(Math.random() * horoscope.fortunes.length)]}`);
            resolve();
        }, 2000);
    });
};

let getHoroscope = async (signIndex) => {
    console.log("Prediction has started");
    await requestSign(signIndex);
    await fetchData();
    await showFortune();
    console.log("Prediction has ended");
};

getHoroscope(Math.floor(Math.random() * horoscope.signs.length));
```

ここで、Promiseは非同期処理の結果を表現するオブジェクトで、その結果がまだ利用できない場合でも参照を保持することができます。async/awaitはPromiseを更に使いやすくするための構文で、非同期処理を同期的に書くことができます。

以上がJavaScriptの非同期処理と、その改善策であるPromiseとasync/awaitについての概要です。ここで紹介したテーマやコードは、非同期処理とそれらのパターンの理解を深めるための一例にすぎません。

更に詳しい説明を学びたい方は、以下のYouTubeの動画をお勧めします："Asynchronous JavaScript Course (Async/Await, Promises, Callbacks)"[^1^]。この動画は非常に分かりやすく、非同期処理の理解を深めるのに大変役立つと思います。

JavaScriptの非同期処理についてはまだまだ議論すべき点が多く存在しますが、この記事が少しでも皆さんの理解の助けになれば幸いです。

[^1^]: https://www.youtube.com/watch?v=ZYb_ZU8LNxs&t=116s