JavaScript学習の非同期

11182 ワード

ここのコード例はここのasyncを参照してください.
目次
  • 引用
  • calback
  • async
  • �Promise
  • Promiseオブジェクト
  • ブルーバード
  • Generator
  • co
  • async/await
  • 小結
  • ことばを引く
    JavaScript言語の実行環境が「単一スレッド」であることはよく知られていますが、同時にプログラムを作成するための敷居が大幅に低くなりました.
    しかし、どうやってハードルを下げながら性能を確保しますか?承諾は異歩である
    したがって、本論文ではJavaScriptの非同期プログラミングの方法を詳しく説明します.
    calback
    calbackはまたコールバックと呼ばれています.JavaScriptプログラミングの中で最も基本的な非同期処理方法です.
    例えば、ファイルのコードを読み込む.
    // callback.js
    var fs = require('fs');
    
    fs.readFile('file1.txt', function (err, data) {
        console.log("file1.txt: " + data.toString());
    
        fs.readFile('file2.txt', function (err, data) {
            console.log("file2.txt: " + data.toString());
    
            fs.readFile('file3.txt', function (err, data) {
                console.log("file3.txt: " + data.toString());
            });
        });
    });
    
    テストファイルの内容はそれぞれ
    // file1.txt
    file1
    
    // file2.txt
    file2
    
    // file3.txt
    file3
    
    babel-nodeを使ってcalback.jsファイルの印刷結果は以下の通りです.
    file1.txt: file1
    file2.txt: file2
    file3.txt: file3
    
    Babel-nodeについてもっと紹介します.JavaScript学習のバージョンを参照してください.
    async
    上記は手順のみで非同期フィードバックを実行する簡単な例です.より複雑な非同期制御を実現するために、第三者のライブラリasyncを利用してもいいです.
    asyncの基本的な制御プロセスは以下の三つです.
    series
    
    parallel
    
    waterfall
    
  • seriesシーケンスは実行されますが、データインタラクションがありません.
    例えば上記のようにファイルを読み込む例はasyncを使って実現されます.
    // async.js
    var fs = require('fs');
    var async = require('async');
    
    async.series([
        function (callback) {
            fs.readFile('file1.txt', function (err, data) {
                callback(null, 'file1.txt: ' + data.toString());
            });
        },
        function (callback) {
            fs.readFile('file2.txt', function (err, data) {
                callback(null, 'file2.txt: ' + data.toString());
            });
        },
        function (callback) {
            fs.readFile('file3.txt', function (err, data) {
                callback(null, 'file3.txt: ' + data.toString());
            });
        }
    ],
        function (err, results) {
            console.log(results);
        });
    
    asyncを使う前にインストールの依存が必要です.npm i--save async
    babel-nodeを使ってasync.jsファイルの印刷結果は以下の通りです.
    [ 'file1.txt: file1', 'file2.txt: file2', 'file3.txt: file3' ]
    
  • parallel並列実行
  • 複数のファイルを同時に読み込む機能を実現したいなら、asyncを使って実現します.
    // async.js
    async.parallel([
        function (callback) {
            fs.readFile('file1.txt', function (err, data) {
                callback(null, 'file1.txt: ' + data.toString());
            });
        },
        function (callback) {
            fs.readFile('file2.txt', function (err, data) {
                callback(null, 'file2.txt: ' + data.toString());
            });
        },
        function (callback) {
            fs.readFile('file3.txt', function (err, data) {
                callback(null, 'file3.txt: ' + data.toString());
            });
        }
    ],
        function (err, results) {
            console.log(results);
        });
    
    babel-nodeを使ってasync.jsファイルの印刷結果は以下の通りです.
    [ 'file1.txt: file1', 'file2.txt: file2', 'file3.txt: file3' ]
    
    ここのファイルは内容が小さいので、結果はまだ順序で実行されているように見えますが、実は並行して実行されています.
  • waterfall順序で実行され、データインタラクションがある
  • // async.js
    var fs = require('fs');
    var async = require('async');
    
    async.waterfall([
        function (callback) {
            fs.readFile('file1.txt', function (err, data) {
                callback(null, 'file1.txt: ' + data.toString());
            });
        },
        function (n, callback) {
            fs.readFile('file2.txt', function (err, data) {
                callback(null, [n, 'file2.txt: ' + data.toString()]);
            });
        },
        function (n, callback) {
            fs.readFile('file3.txt', function (err, data) {
                callback(null, [n[0], n[1], 'file3.txt: ' + data.toString()]);
            });
        }
    ],
        function (err, results) {
            console.log(results);
        });
    
    babel-nodeを使ってasync.jsファイルの印刷結果は以下の通りです.
    [ 'file1.txt: file1', 'file2.txt: file2', 'file3.txt: file3' ]
    
    もちろんasyncの機能はこれらだけではなく、autなどもっと強力なプロセスコントロールなどの読者が知りたいなら、ここを参考にしてください.
    Promise
    簡単なプロジェクトにとって、上述のasync方式を使うと完全に需要を満たすことができます.
    しかし、リピーターベースの方法は複雑なプロジェクトの中ではまだ簡潔ではないです.
    そのため、Promiseに基づく非同期方法が生まれました.
    Promiseを使う前に、Promiseとは何かを明確にする必要がありますか?
    Promiseは非同期プログラミングのための統一インターフェースを提供することを目的としています.
    Promiseを使う時、インターフェースはどのように統一されていますか?
    return step1().then(step2).then(step3).catch(function(err){
      // err
    });
    
    上記の例から、Promiseには以下の三つの特徴があることが分かります.
      Promise
    
        
    
    then/catch    
    
    もちろん上述の手順以外にも、制御フローPromiseは並列実行の制御フローをサポートしています.
    var promise123 = Promise.all([promise1, promise2, promise3]);
    
    Promiseオブジェクト
    Promiseの原理と使用を知ると、Promiseにパッケージされたコードを呼び出すことができます.
    しかし、もし自分でPromiseを封入する必要があったら、どうすればいいですか?
    �ES 6提供のPromiseオブジェクトが使用できます.
    ES 6及びJavaScriptバージョンについての詳細な紹介はJavaScript学習のバージョンを参照することができます.
    例えば、ファイルを読み込むための非同期動作は、以下のようにPromiseオブジェクトにパッケージ化することができる.
    // promise.js
    var fs = require('fs');
    
    var readFilePromise = function readFilePromise(file) {
        return new Promise((resolve, reject) => {
            fs.readFile(file, function (err, data) {
                if (err) {
                    reject(err);
                }
                resolve(file + ': ' + data.toString());
            });
        });
    }
    
    readFilePromise('file1.txt').then(
        function (data) {
            console.log(data);
        }
    ).catch(function (err) {
        // err
    });
    
    babel-nodeを使ってpromise.jsファイルを実行して結果を印刷します.
    file1.txt: file1
    
    ブルーバード
    上記の自分でPromiseオブジェクトをカプセル化する方法以外に、第三者ライブラリbluebirdを借りることができます.
    bluebird以外にもPromiseを実現するための第三のライブラリがあります.例えばq、bluebirdに関するもっと多い比較と紹介はWhat's the difference between Q、Bluebird、and Ayncを参照してもいいです.
    上記Promiseオブジェクトを使用して実現された例については、bluebirdを用いて以下のように実現されています.
    // bluebird.js
    var Promise = require('bluebird');
    var readFile = Promise.promisify(require('fs').readFile);
    
    readFile('file1.txt', 'utf8').then(
        function (data) {
            console.log('file1.txt: ' + data);
        }
    ).catch(function (err) {
        // err
    });
    
    ブルーバードを使用する前に、インストール依存が必要です.npm i--save bluebird
    babel-nodeを使ってbluebird.jsファイルの印刷結果は以下の通りです.
    file1.txt: file1
    
    Generator
    PromiseはCallback Hellの問題を解決できますが、チェーンのコードはまだ直観的ではないようです.
    従ってES 6にはGenerator関数が導入され、またジェネレータ関数と呼ばれる.
    Generator関数と一般関数の違いは、functionの後ろに星番号を追加したということです.すなわち、function*
    例えば、以下のGenerator関数を用いて、ファイルを読み込む例
    // generator.js
    var fs = require('fs');
    
    function* generator(cb) {
        yield fs.readFile('file1.txt', cb);
    
        yield fs.readFile('file2.txt', cb);
    
        yield fs.readFile('file3.txt', cb);
    };
    
    var g = generator(function (err, data) {
        console.log('file1.txt: ' + data);
    });
    
    g.next();
    
    Generator関数は以下の二つの特徴があります.
    Generator関数を呼び出して返したのはGeneratorオブジェクトですが、コードはyieldで実行を一時停止します.
    Generatorオブジェクトのnext()メソッドコードを実行し続けて次のyieldに一時停止します.
    上记のように、コードは一回だけnext()メソッドを実行していますので、file 1.txtを読んでから停止します.
    したがって、Babel-nodeを使ってgenerator.jsファイルの印刷結果は以下の通りです.
    file1.txt: file1
    
    co
    Generator関数は目的は良いですが、理解と使用が不便なので、神器coがあります.
    これはGenerator関数を自動的に実行するために、開発者がGeneratorオブジェクトを手動で作成する必要がないようにし、next()を呼び出す方法です.
    coを使った後に非同期のコードはこのように見えます.
    // co.js
    var Promise = require('bluebird');
    var readFile = Promise.promisify(require('fs').readFile);
    var co = require('co');
    
    co(function* () {
        var data = yield readFile('file1.txt', 'utf8');
        console.log('file1.txt: ' + data);
    
        data = yield readFile('file2.txt', 'utf8');
        console.log('file2.txt: ' + data);
    
        data = yield readFile('file3.txt', 'utf8');
        console.log('file3.txt: ' + data);
    }).catch(function (err) {
        // err
    });
    
    coを使用する前にインストール依頼が必要です.npm i--save co
    babel-nodeでco.jsファイルを実行して印刷します.結果は以下の通りです.
    file1.txt: file1
    file2.txt: file2
    file3.txt: file3
    
    上記の例から、coは以下の2つの特徴があることが分かります.
    co()はPromiseに戻ります.
    coパッケージのGenerator関数の中のyieldの後ろはPromiseでなければなりません.
    上記のcoの基本的な使い方以外にも、coを使ってGenerator関数を普通の関数にカプセル化することができます.
    // co-wrap.js
    var Promise = require('bluebird');
    var readFile = Promise.promisify(require('fs').readFile);
    var co = require('co');
    
    var fn = co.wrap(function* () {
        var data = yield readFile('file1.txt', 'utf8');
        console.log('file1.txt: ' + data);
    
        data = yield readFile('file2.txt', 'utf8');
        console.log('file2.txt: ' + data);
    
        data = yield readFile('file3.txt', 'utf8');
        console.log('file3.txt: ' + data);
    });
    
    fn();
    
    babel-nodeでcow-wrap.jsファイルを実行して結果を印刷します.
    file1.txt: file1
    file2.txt: file2
    file3.txt: file3
    
    ここを見ていると、coがGeneratorに協力するのは本当に非同期的な開発の「究極」だと思います.
    また、coのソースコードは200行以上しかありません.中には多くのコメントと空行が含まれています.
    async/await
    异歩の「究极」に感慨を覚えたばかりだ.coとGeneratorはなぜまだ物语が终わっていないのか?
    理由は簡単です.JavaScript言語の原生も同じようなcoを入れてGeneratorの実現に協力しました.async/await
    ここのasyncはJavaScriptの最新バージョンの中で非同期のキーワードを実現して前に紹介した第三者の倉庫asyncと混同しないでください.
    総じてオリジナルの方がいいですので、co公式ではasync/awaitを使うことを勧めています.
    このことが思い出されます.iPhoneの脱獄プラグインの多くは最新バージョンのiOSに組み込まれていますので、脱獄に興味を持つ人が多くなりました.
    余談はないですが、そのままオリジナルの異歩「究極の神器」を見てみましょう.
    async/awaitを使う前にまずbabelを配置して依存を追加する必要があります.
    npm install --save-dev babel-preset-stage-3
    
    ルートディレクトリに追加します.Babelrcファイルの内容は以下の通りです.
    {
        "presets": [
            "stage-3"
        ]
    }
    
    async/awaitは最新のJavaScriptバージョンstage-3で導入されたES 6はサポートされていません.
    続いてJavaScript言語の原生のasync/awaitを使うことができます.
    // async/await.js
    var Promise = require('bluebird');
    var readFile = Promise.promisify(require('fs').readFile);
    
    var fn = async function () {
        var data = await readFile('file1.txt', 'utf8');
        console.log('file1.txt: ' + data);
    
        data = await readFile('file2.txt', 'utf8');
        console.log('file2.txt: ' + data);
    
        data = await readFile('file3.txt', 'utf8');
        console.log('file3.txt: ' + data);
    };
    
    fn();
    
    上記の例からasync/awaitには以下の2つの特徴があることが分かります.
    async/awaitと一般関数の使い方はほとんど同じです.
    唯一の違いは、functionの前にasyncを加えて関数内のPromiseの前にawaitを加えることです.
    結び目
    最後にもう一度JavScriptの非同期プログラムの完全な進展過程を振り返ってみます.
    callback (async) -> Promsie (bluebird) -> Generator (co) -> async/await (stage-3)
    
    co大神さんの話を聞いて他の案は全部使いません.早くasync/awaitの懐に入れてください.
    参照
  • Async詳細解の一つ:フロー制御
  • 枚の図学会はAsyncコンポーネントを用いて非同期フロー制御を行う
  • .
  • Promiseオブジェクト
  • Javascript非同期プログラミングの4つの方法
  • Node.js最新技術スタックのPromise編
  • Generator関数の意味と使い方
  • yieldとyield*
  • co関数ライブラリの意味と使い方
  • Babel入門教程
  • もっと多くの文章を、私の個人ブログを応援してください.