javascript非同期プログラミング


非同期機構
JavaScriptの実行環境は単スレッドであり、単スレッドの利点は実行環境が簡単であり、資源同期、デッドロックなどのマルチスレッド閉塞式プログラミングを考慮する必要がない.しかし、悪い点は、タスクの実行時間が長い場合、後のタスクは長い時間待ちます.ブラウザ側ではブラウザの仮死、マウスの応答ができないなどの状況があります.したがって、ブラウザ側では、ブラウザの応答を失うことを避けるために、時間がかかります.非同期実行とは、同期実行(プログラムの実行順序とタスクの順序が一致しています.同期されています.)とは異なり、各タスクには1つ以上のコールバック関数があり、前のタスクが終了したら、後のタスクを実行するのではなく、コールバック関数を実行します.したがって、プログラムの実行順序はタスクの並び順とは一致しない、非同期である.Javascriptがシングルスレッドである以上、どうやって非同期的に実行できますか?
Javascriptスレッドモデルとイベントドライバ
JavaScriptにはイベントサイクルに基づく合併モードがあります.このモードはC言語とjavaとは大きく違っています.
実行時の概念
  • スタック関数がスタックフレームの形成を呼び出す.
    function f(b){
        var a = 12;
        return a+b+35;
    }
    
    function g(x){
        var m = 4;
        return f(m*x);
    }
    
    g(21);
    は、関数gを呼び出すと、最初にgパラメータと局所変数を含むフレームを作成する.g関数がf関数を呼び出すと、fパラメータと局所変数とを含む第二のスタックフレームを作成し、第一のスタックフレームの上部にプッシュする.fが戻ると、上部のスタックフレーム要素がイジェクトされる(g呼び出しのみが残る).g関数が戻るとスタックは空です.
  • ヒープは大きな非構造領域であり、対象はヒープに割り当てられている.
  • 列のJavascript実行環境は、一連の実行される情報リストである情報キューを含む.各メッセージは関数に関連付けられている.スタックが空の場合、メッセージキューからメッセージを取り出して処理する.この処理は、関連する関数を呼び出し(したがって、初期化されたスタックフレームを生成する)を含む.スタックが再び空の場合、メッセージ処理は終了する.
  • イベントサイクル
    イベントサイクルの名前は、その実現に由来しています.よく次のようになります.
    while(queue.waitForMessage()){
      queue.processNextMessage();
    }
    queue.waitForMessageは、同期してメッセージを待つ.
  • は、各メッセージの完全な処理が完了した後に実行され、他のメッセージが処理される.このような利点は、1つの関数が前に進められない場合、他の関数の実行が完了するまで待つことができるということです.これはCと違って、例えば、一つの関数がスレッドで動作している場合、他のスレッドのコードを任意の点で実行してもいいです.このようなモードの欠点は、メッセージが長すぎると、Webアプリケーションはクリックやスクロールのようなユーザインタラクションを処理できないことである.このブラウザは「スクリプトにかかる時間が長すぎる」ダイアログを緩和します.良い方法は、メッセージを短く処理することによって、いくつかのメッセージを削減することができます.
  • 追加メッセージは、ウェブブラウザにおいて、イベントをいつでも追加し、イベントが発生し、イベントを伴ってイベントにバインドすることができる.イベントの傍受がないと、イベントはなくなります.要素をクリックするように、要素にイベントをバインドします.setTimeoutを呼び出すと、関数の第2のパラメータ時間が渡されると、メッセージがキューに追加されます.キューに他のメッセージがない場合、メッセージは直ちに処理される.しかし、メッセージがある場合、setTimeoutの情報は、処理のために他のメッセージを待つ必要がある.このため、第二のパラメータは保証時間ではなく最小時間である.
  • いくつかの動作環境の間の通信は、ウェブウォーカーまたはドメインをまたぐiframeに自分のスタック、スタック、およびメッセージ・キューがあります.二つの異なる動作環境は、メッセージをpostMessageで送信するしかない.この方法は、後者がメッセージイベントを傍受する場合、他の実行にメッセージを追加する.
  • ブロックしない
    イベントサイクルモデルはJavascriptの興味深い属性で、他の言語と違って、ブロックしたことがない.ブラウザにはイベントスケジュールのための専用のインスタンスがあると仮定する(この例はスレッドであり、イベント配信スレッドevent dispatch threadと呼ぶことができる).この例の動作は、イベントキューからイベントを取り出し、イベントに関連したコールバック関数を処理することである.なお、コールバック関数は、イベント配信スレッドではなく、Javascriptのメインスレッドで実行され、イベント処理がブロックされないことを保証する.イベントおよびコールバックによるI/O動作は典型的な表現であり、アプリケーションがインデックス型データベースクエリの戻りを待つか、またはXHR要求の戻りを待つとき、ユーザ入力のような他のことをまだ処理することができる.
    コールバック
    フィードバックはjavascriptの基礎であり、関数はパラメータとして伝達される.次のように:
    f1();
    f2();
    f3();
    f 1において大量の時間消費動作が実行される場合、f 2はf 1の後に実行される必要がある.プログラムはコールバック形式に変更できます.以下のとおりです
    function f1(callback){
        setTimeout(function () {
          // f1                i,l,you.
          console.log("this is function1");
          var i = "i", l = "love", y = "you";
            if (callback && typeof(callback) === "function") {
                callback(i,l,y);
            }
        }, 50);
    }
    
    function f2(a, b, c) {
        alert(a + " " + b + " " + c);
        console.log("this is function2");
    }
    
    function f3(){console.log("this is function3");}
    f1(f2);
    f3();
    実行結果:
    this is function 3
    this is function 1 i love youthis is function 2
    このようにして、同期動作を非同期操作に変えました.f 1はプログラムの実行を滞らせません.先にプログラムを実行する主要なロジックに相当します.時間がかかる操作を実行を遅らせます.レギュレータ関数の利点は簡単で軽量級(余分なライブラリは不要)である.欠点は、各部分間の高度結合(Couplling)であり、流れが混乱し、各タスクは一つのコールバック関数しか指定できない.ある操作には複数のブロックではないIO操作が必要で、その結果はすべてコールバックによって、イタリアの麺式のコードが生成されます.
    operation1(function(err, result) {
        operation2(function(err, result) {
            operation3(function(err, result) {
                operation4(function(err, result) {
                    operation5(function(err, result) {
                        // do something useful
                    })
                })
            })
        })
    })
    
    事件の傍受
    もう一つの考えはイベント駆動モードを採用することです.タスクの実行はコードの順序に依存せず、あるイベントが発生するかどうかに依存します.
    // plain, non-jQuery version of hooking up an event handler
    var clickity = document.getElementById("clickity");
    clickity.addEventListener("click", function (e) {
        //console log, since it's like ALL real world scenarios, amirite?
        console.log("Alas, someone is pressing my buttons…");
    });
    
    // the obligatory jQuery version
    $("#clickity").on("click", function (e) {
        console.log("Alas, someone is pressing my buttons…");
    });
    イベントをカスタマイズして傍受することもできます.カスタムイベントについては、他の部分に属します.この方法の利点は、複数のイベントを結びつけることができ、各イベントは複数のコールバック関数を指定することができ、かつ「結合解除」(Decoupling)が可能であり、モジュール化を容易にすることができるということである.欠点はプログラム全体がイベント駆動型になり、動作フローが非常に不明瞭になります.
    観察者モード
    私たちは、あるミッションを実行した「信号センター」があると仮定して、信号センターに信号を送ると、他のタスクは信号センターに「購読」(subscribe)という信号を送って、いつ自分が実行できるかを知ることができます.これは「公開/購読モード」と呼ばれ、また「観察者モード」とも呼ばれています.
    var pubsub  = (function(){
        var q = {}
            topics = {},
            subUid = -1;
        //    
        q.publish = function(topic, args) {
            if(!topics[topic]) {return;}
            var subs = topics[topic],
                len = subs.length;
            while(len--) {
                subs[len].func(topic, args);
            }
            return this;
        };
        //    
        q.subscribe = function(topic, func) {
            topics[topic] = topics[topic] ? topics[topic] : [];
            var token = (++subUid).toString();
            topics[topic].push({
                token : token,
                func : func
            });
            return token;
        };
        return q;
        //        ,  topics,          token,      
    })();
    //     
    var f2 = function(topics, data) {
        console.log("logging:" + topics + ":" + data);
        console.log("this is function2");
    }
    
    function f1(){
        setTimeout(function () {
          // f1     
          console.log("this is function1");
           //    'done'
            pubsub .publish('done', 'hello world');
        }, 1000);
    }
    pubsub.subscribe('done', f2);
    f1();
    上のコードの運転結果は以下の通りです.
    this is function 1
    loging:done:hello world this is function 2
    観察者モードの実現方法はいろいろありますが、直接第三者ライブラリを借りることもできます.この方法の性質は「イベントモニター」と類似している(観察者モードとカスタムイベントは非常に似ている)が,後者より明らかに優れている.観察者モードは、イベント傍受と同様に、良好なマージンを有し、メッセージセンターの処理により、プログラムの実行を良好に監視することができる.
    Promisesオブジェクト
    Promisesの概念は、CommunJSグループのメンバーがPromises/A仕様で提出したものです.Promisesは非同期操作のフィードバックを管理する方法として徐々に用いられてきたが、それらの設計からすれば、それらはそれよりはるかに有用である.Promiseはコードを同期させて、同時にコードの非同期実行を与えます.
    function f1(){
        var def = $.Deferred();
        setTimeout(function () {
           // f1     
            console.log("this is f1");
            def.resolve();  
        }, 500);
        return def.promise();
    }
    
    function f2(){
        console.log("this is f2");
    }
    
    f1().then(f2);
    上のコードの運転結果は以下の通りです.
    this is f 1
    this is f 2
    上記はjqueryによるPromises/Aの実現、jqueryには一連の方法がありますが、具体的には参考になります.Deferred Object.PromisesについてはYou're Missing the Point of Promisesを読むことを強く勧めます.Promise(日本語:承諾)は有限状態のマシンで、3つの状態があります.このうち、pendingは初期状態であり、fulfilledとrejectiedは終了状態である(エンド状態はpromiseのライフサイクルが終了したことを示す).状態変換関係は、pending->fulfilled、pending->rejectiedです.状態が変わると、成功イベントの実行、失敗イベントの実行など、様々なイベントがトリガされます.次のセクションでは、状態マシンのjs非同期プログラミングを実現するために具体的に述べます.
    ステートマシン
    Promisesの本質は、実際に状態マシンによって実現され、非同期操作を対象の状態に変化させてフックを行い、非同期動作が終了すると、対応する状態が変化し、他の操作をトリガする.これは、コールバック関数、イベントの傍受、リリース/購読などの解決策よりも、論理的に合理的で、コードの複雑さを低減しやすいです.Promisesについての参考:JS魔法堂:ソース解析Promises/A仕様.
    ES 6の非同期へのサポート
    これは新しい技術で、2015年のECMAScript(ES 6)基準の一部となっています.この技術の仕様は完了しましたが、実施状況はブラウザによって異なり、ブラウザでのサポート状況は以下の通りです.デスクトップ:携帯端末:
    var f1 = new Promise(function(resolve, reject) {
        setTimeout(function () {
           // f1     
           console.log("this is f1");
           resolve("Success");
            
        }, 500);  
    });
    function f2(val){
        console.log(val + ":" + "this is f2");
    }
    function f3(){
        console.log("this is f3")
    }
    f1.then(f2);
    f3();
    以上のコードはChromeバージョン43で実行された結果:
    this is f 3
    this is f 1 Success:this is f 2
    ES 6のPromiseオブジェクトの特性についてはMDNのPromiseを参照してください.
    その他の参考
    Aynchronous JS:Callbacks、Listeners、Control Flow Libs and Promises Five Patterns to Help You Tame Aynchronous JavaScript Javascriptプログラムの4つの方法はJavascript非同期プログラミングを探求します.