1. ホーム
  2. javascript

[解決済み] コールバック地獄とは何か、RXはそれをどのように、そしてなぜ解決するのか?

2022-07-20 19:44:05

質問

JavaScriptやnode.jsを知らない人のために、コールバック地獄とは何か、簡単な例とともに明確な定義をしていただけないでしょうか。

どのような場合に(どのような設定において)コールバック地獄が発生するのでしょうか?

なぜ発生するのでしょうか?

コールバック地獄は、常に非同期計算と関係しているのでしょうか?

それともシングルスレッドアプリケーションでもコールバック地獄は起こりうるのでしょうか?

CourseraのReactive Courseを受講しましたが、Erik Meijerは講義の中で、RXが"callback hell"の問題を解決してくれると言いました。私はCourseraのフォーラムで"callback hell"とは何かを尋ねましたが、明確な回答は得られませんでした。

簡単な例で"callback hell"を説明した後、その簡単な例でRXが"callback hell problem"をどう解決するのかも教えてください。

どのように解決するのですか?

1) javascriptやnode.jsを知らない人にとって、quot;コールバック地獄とは何でしょうか?

この他の質問には、Javascriptのコールバック地獄の例がいくつかあります。 Node.jsで非同期関数の長いネストを回避する方法

Javascriptにおける問題は、計算をフリーズさせ、残りの部分を後から(非同期に)実行させる唯一の方法が、コールバック内に残りの部分を置くことであるということです。

例えば、次のようなコードを実行したいとします。

x = getData();
y = getMoreData(x);
z = getMoreData(y);
...

getData関数を非同期にしたい場合、つまり、関数が値を返すのを待つ間に他のコードを実行する機会を得たい場合はどうなるでしょうか。Javascriptでは、唯一の方法は、非同期計算に触れるものをすべて、次のように書き換えることでしょう。 継続パッシングスタイル :

getData(function(x){
    getMoreData(x, function(y){
        getMoreData(y, function(z){ 
            ...
        });
    });
});

このバージョンが以前のものよりも醜いことは、私が誰かを説得する必要はないと思います :-)

2) いつ (どのような設定で) "コールバック地獄問題" が発生するのでしょうか。

コールバック関数がたくさんあるときです。コード内にコールバック関数が多ければ多いほど扱いが難しくなり、ループやトライキャッチブロックなどが必要な場合は特にひどくなります。

例えば、私の知る限り、JavaScriptでは一連の非同期関数を実行する唯一の方法は、前の関数が戻った後に実行される再帰的な関数を使うことです。forループは使えません。

// we would like to write the following
for(var i=0; i<10; i++){
    doSomething(i);
}
blah();

ではなく、結局は書く必要があるかもしれません。

function loop(i, onDone){
    if(i >= 10){
        onDone()
    }else{
        doSomething(i, function(){
            loop(i+1, onDone);
        });
     }
}
loop(0, function(){
    blah();
});

//ugh!

StackOverflowに寄せられる、この種のことをどうすればいいのかという質問の数は、それがいかに混乱するものであるかを物語っています :)

3) なぜ起こるのでしょうか?

JavaScript では、非同期呼び出しが返された後に実行されるように計算を遅延させる唯一の方法は、コールバック関数内に遅延コードを置くことであるため、この現象が発生します。従来の同期スタイルで書かれたコードを遅延させることはできないので、いたるところにネストされたコールバックが存在することになります。

4) あるいは、シングルスレッド アプリケーションでもコールバック地獄が発生することがあるのでしょうか。

非同期プログラミングは並行処理に関係し、シングルスレッドは並列処理に関係します。この 2 つのコンセプトは実際には同じものではありません。

シングルスレッドのコンテキストでも、同時実行のコードを持つことはできます。実際、コールバック地獄の女王であるJavaScriptはシングルスレッドです。

並行処理と並列処理の違いは何ですか?

5) その簡単な例で、RX がどのように "コールバック地獄問題" を解決するのかも示してください。

私はRXについて特に何も知りませんが、通常この問題は、プログラミング言語の非同期計算のネイティブサポートを追加することで解決されます。実装はさまざまで、非同期、ジェネレーター、コルーチン、および callcc が含まれます。

Pythonでは、前のループの例を次のようなもので実装することができます。

def myLoop():
    for i in range(10):
        doSomething(i)
        yield

myGen = myLoop()

これは完全なコードではありませんが、誰かが myGen.next() を呼び出すまで "yield" が for ループを一時停止させるということです。重要なのは、for ループを使用してもコードを書くことができるということです。 loop 関数のようにロジックを裏返す必要がないことです。