非同期呼び出しは、本当にそんなに怖くないです.

12043 ワード

非同期プログラミングは開発初心者にとって、悪夢のような存在ですが、非常に重要で、学ばなければなりません.実はね、非同期の呼び出しはそんなに怖くないです.
伝統的な方法
  • コールバック関数
  • 事件の傍受
  • リリース/購読
  • Promiseオブジェクト
  • 基本概念
    非同期
    非同期とは、簡単に言えば、タスクは一度に連続して行われるものではなく、途中に他のプログラム演算を加えて、第一段階でデータを準備してから戻って計算します.
    コールバック関数
    コールバック関数の名前は、フロントエンドの開発をした仲間が見たことがあると信じています.javaScript言語の非同期プログラミングの実現はコールバック関数です.例を挙げます
    fs.readFile('etc/passwd','utf-8',function(err,data){
        if(err) throw err;
        console.log(data);
    });
    
    Promise
    コールバック関数自体は問題ありません.問題は、複数のコールバック関数がネストされています.つまり、私たちが通称するコールバック地獄です.
        let url1 = 'http://xxx.xxx.1';
        let url2 = 'http://xxx.xxx.2';
        let url3 = 'http://xxx.xxx.3';
        $.ajax({
            url:url1,
            error:function (error) {},
            success:function (data1) {
                console.log(data1);
                $.ajax({
                    url:url2,
                    data:data1,
                    error:function (error) {},
                    success:function (data2) {
                        console.log(data2);
                        $.ajax({
                            url:url3,
                            data,
                            error:function (error) {},
                            success:function (data3) {
                                console.log(data3);
                            }
                        });
                    }
                });
            }
        });
    
    このコード自体のロジックは実現できます.問題は次のいくつかの方面にあります.
  • コードがぶくぶくしています.
  • 可読性の差.
  • 結合度が高すぎて、維持性が悪いです.
  • コード多重性差.
  • はバグを発生しやすいです.
  • 異常はコールバックでしか処理できません.
  • 以前の章ではPromis関数について述べましたが、彼は典型的な解決関数非同期問題で発生しました.彼はコールバック関数の新しい書き方です.
    例を挙げます
    function request(url,data = {}){
        return new Promise((resolve,reject)=>{
            $.ajax({
                url,
                data,
                success:function (data) {
                    resolve(data);
                },
                error:function (error) {
                    reject(error);
                }
            })
        });
    }
    
    let url1 = 'http://xxx.xxx.1';
    let url2 = 'http://xxx.xxx.2';
    let url3 = 'http://xxx.xxx.3';
    request(url1)
        .then((data)=>{
            console.log(data);
            return request(url2,data)
        })
        .then((data)=>{
            console.log(data);
            return request(url3,data)
        })
        .then((data)=>{
            console.log(data)
        })
        .catch((error)=>{
            console.log(error);
        });
    
    
    上記に対してはPromisオブジェクトを使用するとコード構造が明確になります.しかし、問題は一見して、多くのthenがあります.そして、注釈を書かないと、どういう意味なのか分かりにくいです.
    Generator関数
    定義:
    generator関数はES 6が提供する非同期プログラミングソリューションで、文法は従来の関数とは全く違っています.Generator関数を実行すると、エルゴードオブジェクトが返されます.戻ってきたエルゴードオブジェクトは、Generator関数の内部の各状態を順次巡回することができます.
    上記の章では、Gener tor関数の最大の特徴は、関数の実行権を渡すことができると述べました.つまり、実行関数を一時停止することができます.
    function * gen(x) {
      var y  = yield  x + 1;
      return y;
    }
    var g = gen(1);
    g.next(); // {value:2,done:false}
    g.next(); // {value:undefined.done:true}
    
    gen関数を実行して、関数ポインタを返します.ポインタ関数のnextメソッドを呼び出して、内部関数のポインタを移動して、最初に会ったyield文を指します.
    非同期パッケージ
    次はGenerator関数を使って現実的な非同期タスクを実行します.
      var fetch = require('node-fetch);
      
      function * gen() {
        var url = 'http://api.github.com';
        var result = yield fetch(url);
        console.log(result.bio);
      }
      
      var g = gen();
      var result = g.next();
      
      result.value.then(function(data) {
        return data.JSON();
      }).then(function(data) {
        g.next(data);
      })
      
    
    上のコードは非同期で表現されますが、プロセス管理が不便で、自分でnextメソッドを呼び出します.
    Generator関数自動フロー管理
    例:
    function * gen() {
      //...
    }
    
    var g = gen();
    var res = g.next();
    while(!res.done){
        console.log(res.value);
        g.next()
    }
    
    上のコードは自動的に完成できます.前提は非同期です.前のステップを必ず実行してから後のステップを実行しなければならないなら、上の自動実行はだめです.
    Thunk関数
    どうやって自動的に非同期関数を完成しますか?私達の主役を引き出して、thunk関数.
    var fs = requier('fs');
    var thunkify = require('thunkify');
    var readFileThunk = thunkify(fs.readFile);
    
    var gen = function *() {
      var r1 = yield readFileThunk('/etc/fstab');
      console.log(r1.toString());
      var r2 = yield readFileThunk('/etc/shells');
      console.log(r2.toString());
    } 
    
    上記のコードは非同期操作を実行した後、実行権をGenerator関数に返す必要があります.まず手動で実行します.
    var g = gen();
    
    var r1 = g.next();
    r1.value(function(err,data) {
      if(err) throw err;
      var r2 = g.next(data);
      r2.value(function(err,data) {
        if(err) throw err;
        g.next(data);
      })
    })
    
    
    上のコードをよく見てください.generator関数が実行する過程は、同じコールバック関数をnextメソッドに繰り返し入力するvalue属性です.この操作はどうやって自動的に終わりますか?
    function  run (fn) {
      var gen = fn();
      function next(err,data) {
        var result = gen.next(data);
        if(result.done) return ;
        result.value(next)
      }
      next();
    }
    
    function* g() {
      //...
    }
    
    run(g);
    
    上のコードのrun関数はGenerator関数の自動実行器です.内部のnext関数はThunkのコールバック関数で、nextは先にポインタをGenerator関数の次のステップに移動して、それからGenerator関数が終わるかどうかを判断して、もし終わらないならば、next関数を更にthunk関数に伝えて、さもなくば直接退出します.
    これは便利です.実行する関数を、run()関数に入れるだけでいいです.母はもう私の異変を心配しなくなりました.
    CO
    毎回run関数を書きますか?お腹が空きました.COを試してみます.coモジュールは有名なプログラマTJ Holowaychukが2013年6月に発表したツールで、Generator関数の自動実行に使用されます.どう使いますか?かなり簡単です
    var co = require('co');
    co(gen);
    
    なかなか便利です.また、co関数はPromiseオブジェクトを返します.thenメソッドを呼び出してコールバック関数を追加できます.
    co(gen).then(function() {
      console.log('          。')
    });
    
    なぜcoは自動的に実行できますか?
    generator関数は非同期実行容器である.その自動実行には、非同期操作が結果を得た場合、自動的な実行権が必要です.説明してください
    function * gen(x) {
      var y = yield x = 1;
      var z = yield 1+1;
      return y, z;
    }
    
    最初のyieldに実行すると、nextメソッドを呼び出して、値を取得します.nextを再度呼び出して、実行を継続して、再度このステップを呼び出して、人為的に呼び出さなくてもいいです.coモジュールとは、thunk関数とpromiseオブジェクトを一つのモジュールに包装することです.使用する前提は、yieldの後の表現はthunk関数またはpromiseオブジェクトでなければなりません.4.0以降のcoバージョンはpromiseオブジェクトのみに対応します.
    時間のあるクラスメートはcoのソースコードを見てもいいです.簡単です.ここではあまり紹介しません.
    転載先:https://juejin.im/post/5d08af35e51d45554877a5e0