Premis—Deep Dive

11736 ワード

プミスラン


JavaScriptは、コールバック関数を非同期処理のモードとして使用します.しかし、従来のコールバックモードは、コールバックhellの可読性が悪いため、非同期処理で発生するエラー処理が困難であり、複数の非同期処理を一度に処理することにも一定の限界がある.
ES 6はまた、非同期処理の別のモードとしてプロセスを導入する.promisの利点は,従来のコールバックモードの欠点を補い,非同期処理時点を明確に示すことである.

コールバックモードの欠点


コールバックヘリコプター


まず,同期と非同期処理モデルを簡単に振り返ってみよう.
同期処理モードはシリアル実行タスクです.つまり、タスクが順番に実行され、タスクが実行中の場合、次のタスクは実行を待機します.例えば、サーバからデータを取得して表示画面3のタスクを実行すると、サーバにデータを要求して応答するまで、以降のタスクはブロックされる.
非同期処理モードは、タスクを並列に実行します.すなわち,タスクがまだ終了していなくても,待つことなく,直ちに次のタスクを実行する.たとえば、サーバからデータを取得し、画面に表示されるタスクを実行する前に、サーバにデータを要求し、サーバがデータに応答するのを待つのではなく、すぐに次のタスクを実行します.その後、サーバがデータに応答するとイベントが発生し、イベントハンドラはタスクを続行してデータを取得します.JavaScriptのほとんどのDOMイベントとTimer関数(settimeout,setInterval)とAjaxリクエストは非同期処理モードで動作します.
JAvascriptで頻繁に使用される非同期処理モデルは、他の要求をブロックすることなくパラレル処理要求を有する利点がある.
しかし,非同期処理のためにコールバックモードを用いると,処理順序を保証するために複数のコールバック関数がネストされ,より複雑度の高いコールバック地獄が生じるという欠点がある.callback地獄は可読性を悪くし、ミスの原因になる.
콜백지옥
step1(function(value1){
  step2(function(value2){
    step3(function(value3){
      step4(function(value4){
      });
    });
  });
});
長風でもない.
コールバックヘルプの理由を表示すると、非同期処理モードは、実行の完了を待たずに次のタスクをすぐに実行します.したがって、非同期関数で処理結果(またはグローバル変数に割り当てられた)が返されると、予想通りの操作は行われません.
<html>

<body>
    <script>
        // 비동기 함수
        function get(url) {
            // XMLHttpRequest 객체 생성
            const xhr = new XMLHttpRequest();

            //서버 응답 시 호출될 이벤트 핸들러
            xhr.onreadystatechange = function () {
                //서버 응답 완료가 아니면 무시
                if (xhr.readyState !== XMLHttpRequest.DONE) return;

                if (xhr.status !== 200) { //정상응답
                    console.log(xhr.response);
                    // 비동기 함수의 결과에 대한 처리는 반환할 수 없다.
                    return xhr.response; // 1
                } else { //비정상 응답
                    console.log('Error: ' + xhr.status)
                }
            };

            // 비동기 방식으로 Resquest 오픈
            xhr.open('GET', url);
            // Request 전송
            xhr.send();
        }
		// 비동기 함수 내의 readystatechange 이벤트 핸들러에서 처리 결과를 반환(1)하면 순서가 보장되지 않는다.
        const res = get('http://example.com');
        console.log(res); // 2.undefined
    </script>
</body>

</html>
非同期関数のreadystatechangeイベントハンドラが処理結果を返す場合、順序は保証されません.すなわち,2でget関数が返す値は参照できない.原因を見てみましょう.
get関数を呼び出すと、get関数の実行コンテキストが作成され、呼び出しスタック(実行コンテキストスタック)で実行されます.get関数が返すxhr.responseはreadstatechangeイベントハンドラによって返されます.readystatechangeイベントは発生時間が不明ですが、get関数が終了した後に発生する必要があります.get関数の最後の文xhr.send()が実行された後にのみ、readystatechangeイベントが発生するため、リクエストと送信リクエストが送信されます.
get関数が終了するとすぐにconsoleが呼び出されます.log(2)が呼び出され、呼び出しスタックに入って実行される.console.イベントハンドラは、logを呼び出す前にreadstatechangeイベントが発生していてもコンソールです.logの前には実行されません.
readystatechangeイベントのイベントハンドルは、イベント発生時に直ちに実行されません.イベントが発生すると、いったんTastkuに入り、呼び出しスタックが空になると、イベントループから呼び出しスタックに入って実行される.console.ログ呼び出しポイントの前にreadystatechangeイベントが発生していても、get関数が終了するとすぐにコンソールが呼び出されます.readystatechangeイベントのイベントハンドラは、ログが呼び出され、呼び出しスタックに入るためコンソールです.ログは呼び出しスタックを終了して実行されます.get関数の後がconsoleの場合.ログが100回呼び出されると、readystatechangeイベントのイベントハンドラはすべてのコンソールになります.ログが終了してからのみ実行されます.
したがって,get関数の戻りぬかを用いて後続処理を行うことはできない.すなわち,非同期関数の処理結果を返すと,順序が保証されないため,後続の処理はできない.すなわち,非同期関数の処理結果の処理は,非同期関数のコールバック関数内で行わなければならない.
そのためcallback地獄が発生します.
非同期関数の処理結果で他の非同期関数を呼び出す必要がある場合、関数呼び出しが重なり、複雑度が高まる現象が発生します.これがコールバック地獄です
callback地獄はコードの可読性を悪くし,複雑さを増し,エラーの原因となり,エラー処理が困難になる.

エラー処理の制限


ダイヤルバック非同期処理に存在する問題の中で,最も深刻なのはエラー処理の困難である.
try{
	setTimeout(() => { throw new Error('Error!'; },1000);
} catch(e) {
	console.log('에러를 캐치하지 못한다...');
    console.log(e);
tryブロックのsettimeout関数が失われた場合、1秒後にコールバック関数が実行され、このコールバック関数に異常が発生します.ただし、この例外はcatchブロックでは捉えられません.原因を調べてみましょう.
非同期処理関数のコールバック関数は、このイベント(timer関数のtickイベント、XMLHttpRequestのreadystatechangeイベントなど)が発生したときにタスクキューに移動され、呼び出しスタックがアイドルのときに呼び出しスタックに移動されて実行される.settimeout関数が呼び出しスタックから削除されました.
これはsettimeout関数を呼び出すコールバック関数がsettimeout関数ではないことを意味する.settimeout関数のコールバック関数の呼び出し元がsettimeout関数である場合、呼び出しスタックにsettimeout関数が存在する必要があります.
異常は呼び出し者方向に伝播する.ただし、上述したようにsettimeout関数を呼び出すコールバック関数はsettimeout関数ではない.したがって、settimeout関数のコールバック関数で発生したエラーはcatchブロックでは取得されず、プロセッサは終了します.
これらの問題を克服するためにpomiseが提案された.PromiseはEs 6で正式に採用され、IE以外の多くのブラウザをサポートしている.