js非同期プログラミングをプレイ

10693 ワード

js非同期プログラミング
一般的には、js足どりの言語の実行環境は単スレッドであることがわかっています.つまり、一つのタスクが完了するまで、二つ目のタスクを行い、そして下にずっと進行します.このような実行環境は簡単ですが、複雑な運用はできません.一つの要求が非常に長い間必要となると、次のプロセスは座礁されます.プロセスはこのようにして崩れました.
このハード需要を解決するために、Javascript言語は2つの言語パターンを提案しています.同期(Synchronous)と非同期(Aynchronous).
非同期のいくつかの一般的な方法
  • コールバック関数
  • 購読とリリースモード
  • Promise
  • generator
  • async/await
  • コールバック関数の方法
    一つの関数をパラメータとして別の関数に導入することで、一定の条件を満たすと、calback関数が実行されます.
    使い方:
    //            
    function fn1(a, fn) {
      if(a > 10 && fn instanceof Function) {
        fn.call()
      }
    }
    function fn2() {
      console.log(' --- fn2 ----')
    }
    //          
    function fn3(fn) {
      setTimeout(() => {
        console.log('--- fn3  ---')
        fn.call()
      },1000)
    }
    //         
    fn1(12, fn2)
    fn3(fn2)
    
    非同期をコールバック関数によって処理することは、非同期の初期の場合であり、jqueryの多くは、コールバックによってコールバックを実現する.しかし、このようなモードコードは比較的結合されており、コードの維持には不利である.
    購読モードを公開
    pub/subモードはjs設計モードの一つで、自身はjavaに鑑みてのモードですが、非同期処理をする時に非常に役立ちます.一つの情報センターEventCenterによって処理される傍受(on)とトリガ(triggle).前の文章を参考にして、最後のデザインモードを作成します.
    function fn1() {
      setTimeout(() => {
        //          data
        let data = fetch(.....)
        //        waterFull,   data
        Event.triggle('waterFull', data)
      },2000)
    }
    fn1()
    Event.on('waterFull', (data) => {
      //               
      console.log(data)
    })
    
    pub/subモードを通じて、私達は情報センターで信号の出所がはっきり見えます.便利な集中管理は、モジュール化の管理にもっと便利です.しかし、プロジェクト全体でpub/subモードを使うと、流れがよく分かりません.データの入手とデータの処理が分離して、後期のメンテナンスにも大きな問題があります.
    Promise
    今の基本的な前端人員については、Promiseを聞いたことがないと言っていますが、もしプロミスをたくさん見ていなかったら、阮先生のes 6文書Promiseを確認できます.以下は主に具体的な要求によってpromiseを実現します.詳しく説明しません.Promiseコンストラクタは承諾となり、3つの状態resolverejectpendingに分けられ、一旦状態がpendingから他の2つの状態に変化したら、修正できなくなり、一つの承諾と同じである.Promiseは、2つのパラメータresolveを受信し、rejectは、成功後の実行および失敗後の実行をそれぞれ表し、インスタンスthen()の方法で関数を伝達することができる.
    //     Promise  
    const promise = new Promise((resolve, reject) => {
      // some code           
      if(success) resolve(value)
      else reject(err)
    })
    promise.then(/*  */(data) => { console.log(data) }).catch(/*  */(err) => { console.log(err) })
    
    ここで見たら、これは非同期処理と何か関係があると言うかもしれませんか?あなたが考えてみてください.非同期操作の後、いつ終わるかは気にしなくてもいいです.一人で約束したように、私は彼の約束通りにします.この件はもう処理されました.便利ではないですか?
    let promise = new Promise((resolve, reject) => {
      let data = fetch('url') //          
      resolve(data)
    })
    promise.then(data => console.log(data));
    
    // fetch      promise
    fetch('http://ons.me/tools/dropload/json.php?page=0&size=4').then(response => response.json()).then(data => console.log(data)) //            
    //
    
    私は全然心配しなくてもいいです.その中はどうやって実現されましたか?どうせ約束したら結果をくれます.私はthen()方法で受け入れるだけでいいです.
    Promise.resolive valueは3つの値があります.
  • 単一値
  • promsieの例
  • つのthenableオブジェクト
  • Promise.resolve(value).then((value) => {})
    
    一つの要求が他の要求に依存する場合を処理する.
    もし一つの要求の結果が次の要求のパラメータであれば、オリジナルの要求方法を使えば、右矢印のような反転地獄が出現します.
    何重にもはまっていて、非常に怖いです.メンテナンスには不便です.prmiseを通じて地獄に戻りますか?
    function send(url) {
      return new Promise((resolve, reject => {
        ajax(data);
        if('  ') resolve(data)
        else reject
      }))
    }
    send('url1').then(data => send('url2'))
                .then(data => send('url3'))
                .then(data => send('url4'))
                .then(data => console.log(data)) //      
    
    //          
    Promise.resolve(1).then(val1 => val1+2).then(val2 => val2+3).then(val3 => console.log(val3)) //6
    
    上記のリターン地獄は見ていても便利ですか?コードも簡単です.依存性も強いです.その後もasync/awaitを通して簡略化していきます.
    複数の要求を同時に処理する場合(サーバの返却順序は不要)Promise.all(arr)は、1つのpromiseのインスタンスの配列を受け入れ、複数の要求をサーバに同時に提供することができるが、これはサーバの処理速度に依存する、受信された順序を保証することができない.
    //        url   ,            setPromise      promise   ,    promsie  
    let urlArr = [url1, url2, url3]
    Promise.all(urlArr.map(url => setPromise(url))).then(data => console.log(data))
    //        ,            。
    
    複数の要求を同時に処理し、データを返す順序を確保する必要がある(運用シーンが少ない)
    上記の方法は要求の返却結果が保証されていません.あなたが送った順番で戻ってきます.もし完全な応答の結果を私の希望の順序で返したいなら、どうすればいいですか?
    let urlArr = [url1, url2, url3];
    let totalData = []
    //       ,             ,    Promise.
    urlArr.reduce((promise, url) => {
      return promise.then(() => setPromise(url)).then(data => { totalData.push(data) })
    }, Promise.resolve()) 
    
    このようにして、各要求が完了するのを待って、得られたデータpushからtotalDataの中に、私たちが望む値を順番に得ることができる.もちろんasync/awaitを使うともっと便利です.後で説明します.
    generatorコンストラクタ
    generatorは、1つのコンストラクタであり、generatorは、関数の内部部分を実行するのではなく、コンストラクタオブジェクトに戻り、コンストラクタオブジェクトのnext()を介して関数本体を呼び出し、yieldに会うたびに実行を停止し、オブジェクトに戻る.
    function* gen() {
      console.log(`---- start ---`)
      yield 1
      yield 2
      return 3
    }
    let g = gen() //      generator  ,         
    g.next()  // console---- start --- return { value: 1; done: false }
    g.next() // {value: 2; done : false}
    g.next() // {value: 3; done: true}
    g.next() // {value: undefined; done: true}
    
    yield自体は、コンテンツを後悔することはないので、ビルダオブジェクトにコンテンツを返しただけで、yield表現もコンテンツに戻りたいなら、次のnext()にパラメータを伝えることができます.
    function* gen() {
      let a = yield 1
      console.log(a)
      yield 2
      return 3
    }
    let g = gen();
    //      yield 1       
    g.next() // {value: 1, done: false}  
    //          , yield 1      ggg
    g.next('ggg') // console  ggg  {value: 2, done: false} 
    
    next()を通してパラメータを伝達することで、値を内部に伝達することができ、後の非同期処理に役立つ.
    generator非同期運用
    コンストラクタの一時停止と継続の機能を利用して、非同期要求をうまく処理して、データを得てから他の内容を行うことができます.yield式を用いてpromiseオブジェクトを返す原理です.
    function* send() {
      let data = yield fetch('https://suggest.taobao.com/sug?code=utf-8&q=%E6%89%8B%E6%9C%BA');
    }
    let objData;
    //   
    send().next().value.then( response => response.json()).then(data => objData = data)
    
    このようにインタフェースで要求されたデータを入手しました.これまでのプロミス関数の書き込みよりもずっと簡単です.同期と同じ操作です.
    得られたデータを内部的に処理したいなら?
    //              ,    yield   
    function* send() {
      let data = yield fetch('https://suggest.taobao.com/sug?code=utf-8&q=%E6%89%8B%E6%9C%BA');
      data.result.map(item => {
        return item.push(11)
      })
      return data
    }
    let objData;
    let gen = send()
    //      promise     。
    gen.next().value.then( response => response.json()).then(data => gen.next(data)).then(data => objData=data)
    
    //     
    var gen = function* (){
      var f1 = yield readFile('/etc/fstab');
      var f2 = yield readFile('/etc/shells');
      console.log(f1.toString());
      console.log(f2.toString());
    };
    //       
    const g = gen()
    g.next().value.then(data => {
      //           
      g.next(data).value.then(data => {
        //           
        g.next(data);
      })
    })
    
    簡単なcoモジュール処理generator複数の関数要求
    上記の私の呼び出し方法から分かるように、Promise + generatorの非同期処理を利用して、データをthen()の方法で処理し続けている.一つの方法がありますか?私は直接に関数を実行してほしい値を得ることができます.たとえば:
    function* send() {
      let data = yield fetch('https://suggest.taobao.com/sug?code=utf-8&q=%E6%89%8B%E6%9C%BA');
      return data
    }
    
    run(send) //              data  
    // TODO
    function run(gen) {
      const g = gen();
      function next(data) {
        let result = g.next(data);
        //       ,     value
        if(result.done) return result.value
        result.value.then(data => {
          //     
          next(data)
        })
      } 
      next()
    }
    
    オンラインでは、多くの方法がカプセル化されています.例えば、一般的なrunライブラリでは、このような処理方法がco関数です.しかし、私たちが複数の要求を送る時、あなたはこのように書くかもしれません.
    function* send() {
      var p1 =yield  request( "http://some.url.1" );
      var p2 =yield  request( "http://some.url.2" );
      var r3 = yield request(
            "http://some.url.3/?v=" + r1 + "," + r2
      );
      console.log(r3)       
    }
    //         run  
    run(send)
    
    このように書くと要求が送信されますが、複数の要求を同時にするのではなく、最初の要求p 1を待ってから、第二の要求p 2を行います.性能最適化の面では不利です.また、私たちの要求に合わないです.どうやって2つの要求が独立しますか?また、2つの要求の結果を得ることによって、他の要求を行うことができます.私たちはこのようにすることができます.
    function* send() {
      //        
      var p1 = request( "http://some.url.1" );
      var p2 = request( "http://some.url.2" );
      //        ,            yield  
      const d1 = yield p1;
      const d2 = yield p2;
      var r3 = yield request(
        "http://some.url.3/?v=" + d1 + "," + d2
      );
    }
    
    このように書くのは前に書いたPromise.all()と似ていますか?このように変更できます.
    function* send() {
      //        ,        
      const [d1, d2] = yield Promise.all([
        request( "http://some.url.1" ),
        request( "http://some.url.2" )
      ])
      var r3 = yield request(
        "http://some.url.3/?v=" + d1 + "," + d2
      );
    }
    
    async/await非同期処理
    ES 7は、同期コードのように非同期的な動作が簡単で便利になるように、async/awaitの内部にasync/awaitをカプセル化した処理があるため、全generatorを用いて非同期的な処理をする人は少ないが、非同期の推進においてgeneratorが大きな役割を果たしている.
    await:後にpromiseの例を受けます.
    *async:promiseオブジェクトを返します.*
    簡単な非同期要求
    async function f() {
        //             ,            。
        let data = await fetch('').then(res => res.json()) 
        console.log(data) //     
        return data  //     promise  
    }
    
    async function h() {
      let data = await Promise.resolve(22);
      console.log(data);  // 22
      return data  // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 22}
    }
    
    async function c() {
      try {
        let data = await Promise.reject(22);
        console.log(11) //     
      } catch(e){
         console.log(222)  //     222
      } 
      return 333  // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 333} 
    }
    
    上の例はgeneratorの中の非同期要求と似ていますか?同期のようにコードを作成することができますが、generatorよりもawaitの後にpromiseを追加して直接に該当データを返します.
    複数の要求を同時に処理する場合(サーバの返却順序は不要)
    ASync/awaitで複数の要求を同時に処理します.awaitの後にgenerator例を追加する必要があるので、頭の中で一気にPromise例を思いつきましたか?
    // request    promise  
    async function send() {
      //        ,        
      const [d1, d2] = await Promise.all([
        request( "http://some.url.1" ),
        request( "http://some.url.2" )
      ])
    }
    
    気になるかもしれませんが、どうしてPromise.all()のように追加の関数で呼び出す必要がないですか?
    複数の要求を同時に処理し、データを返す順序を確保する必要がある(運用シーンが少ない)
    データの中にお互いの連絡がないと、また一つずつ送りたいです.これでいいです.
    let patharr = [url1, url2, url3]
    async function main2() {
      let arrData = [];
      //   for        
      for(const url of pathArr) {
        arrData.push(await request(url));
      }
      return arrData
    } 
    
    異歩の好文
    図解async/awati