JavaScript非同期処理のいくつかの方法

32788 ワード

1.コールバック関数
コールバック(callback)は、1つの関数が1つのパラメータとして別の関数に渡され、その関数が実行された後に実行される.2つの関数f 1とf 2があり、f 2はf 1の実行結果を待っていると仮定し、f 1()->f 2();f 1が時間がかかる場合は、f 1を書き換え、f 2(矢印関数)をf 1のコールバック関数と書くことができます.
function f1(callback){
  setTimeout(() => {
    let name = '  '
    console.log('   ') // f1     
    callback(name)
  }, 100);
}
f1((name)=>{
	console.log('  '+name)
})
//    
//     

利点:簡単、便利、実用的.欠点:コールバック関数地獄を形成しやすい.非同期操作が1つしかない場合、コールバック関数で処理するのは全く問題ありません.しかし、複数のコールバック関数をネストすると、複数の非同期操作が強い結合を形成し、コードが混乱し、管理できないため、問題が大きくなります.このことを「コールバック関数地獄」(callback hell)と呼ぶ.
getData('XXX1', () => {
  // callback    
  getData('XXX2', () => {
    // callback    
    getData('XXX3', () => {
      // callback    
      getData('XXX4', () => {
        // callback    
        getData('XXX5', () => {
          // callback    
        })
      })
    })
  })
})

2.Promiseオブジェクト(推奨)
意味:Promiseは非同期プログラミングのソリューションで、利点:Promiseはチェーンプログラミングで、頭が痛いコールバック地獄問題を効果的に解決し、Promiseの結果は成功と失敗の2つの状態があり、非同期操作の結果だけが、現在どの状態であるかを決定することができ、外部のいかなる操作もこの状態を変えることができない.2.1共通API resolve非同期操作成功の結果reject非同期操作失敗の結果then実行Promise状態成功の操作catch実行Promise状態失敗の操作finally失敗の操作
//ES6   ,Promise         ,    Promise  。
    const p = new Promise(function(resolve,reject){
        if(success){
            resolve('     ')
        }else{
            reject('     ')
        }
    })
    p.then(function (res) {
        //   resolve     ,    

    },function (err) {
        //   reject     ,    
    })
    p.catch(function (err) {
        //   reject          then()       ,    
    })
    p.finally(function(){
        //          
    })
const p = new Promise((resolve, reject) =>{
  setTimeout(() => {
    let name = '  '
    resolve(name)
  }, 100);
});
p.then((res) => {
  console.log(res)
})

2.2 Promise.all Promise.allメソッドは、複数のPromiseインスタンスを新しいPromiseインスタンスにパッケージするために使用されます.const p = Promise.all([p 1,p 2,p 3])pの状態はp 1,p 2,p 3によって決定され,二つの場合に分けられる.(1)p 1,p 2,p 3の状態のみがfulfilledとなり、pの状態がfulfilledとなる.このときp 1,p 2,p 3の戻り値は配列をなしてpのコールバック関数に渡される.(2)p 1,p 2,p 3のうちの1つがrejectedされる限り、pの状態はrejectedとなり、このとき最初のrejectされたインスタンスの戻り値はpのコールバック関数に渡される.
let p1 = new Promise((resolve, reject) => {
  console.log('begin p1');
  setTimeout(() => {
    console.log('end p1');
    resolve('p1');
  }, 2);
});
p1.then(() => {
  console.log('then p1');
})

let p2 = new Promise((resolve, reject) => {
  console.log('begin p2');
  setTimeout(() => {
    console.log('end p2');
    resolve('p2');
  },3);
})
p2.then(() => {
  console.log('then p2');
})

setTimeout(() => {
  console.log('setTimeout: p3');
  let p3 = Promise.all([p1, p2]).then((datas) => {
    console.log('begin p3');
    console.log('end p3');
  });
  p3.then(() => {
    console.log('then p3');
  });
},1);
console.log('mark p4');

// begin p1
// begin p2
// mark p4
// setTimeout: p3
// end p1
// then p1
// end p2
// then p2
// begin p3
// end p3
// then p3

3.イベントリスニング
イベント駆動モードを使用すると、タスクの実行はコードの順序に依存せず、イベントが発生するかどうかに依存します.リスニング関数は、on,bind,listen,addEventListener,observeの例であり、f 1にイベントをバインドする(jquery書き方):f 1.on(‘done’,f 2);すなわち、f 1にdoneイベントが発生すると、f 2が実行される.
function f1(){
  settimeout(function(){
    // f1     
    f1.trigger('done');  //      ,    done  ,      f2
  },1000);
}

利点:理解しやすく、複数のイベントをバインドすることができ、各イベントは複数のコールバック関数を指定することができ、結合することができ、モジュール化の欠点を実現するのに有利である:プログラム全体がイベント駆動型になり、実行プロセスが不明確になる
4.Generator関数
意味:Generator関数はES 6が提供する非同期プログラミングソリューションであり、文法的挙動は従来の関数とは全く異なる.基本的な使い方:
    function* helloGenerator() {
      yield 'hello';
      yield 'Generator';
      return 'over';
    }
    
    let hw = helloGenerator();
    hw.next() // {value:"hello",done:false}
    hw.next() // {value:"Generator",done:false}
    hw.next() // {value:"over",done:true}
    hw.next() // {value:undfined,done:true}

特徴:
  • functionキーワードと関数名の間にアスタリスクがあります.
  • 関数体の内部でyield式を使用して、異なる内部状態を定義します.
  • はnext法により各セグメント状態の戻り結果を取得し,上で4回に分けてGennarator関数を実行した.初めて、最初のyield関数の戻り結果を取得して一時停止し、done:falseは、関数内の状態がまだ実行されていないことを表します.2回目、同じ理屈.3回目はreturnの戻り結果を取得し、done:trueは関数内の状態が実行済みであることを示す.4回目は、関数内に実行可能な状態がなく、undfinedを返し、関数内の状態が実行済みであることを伝える.関数内にreturnがない場合、3回目にundfinedが返され、done:trueは関数内の状態が実行済みであることを示します.

  • 5.async関数(推奨)
    意味:async関数はES 2017規格に導入され、asyncは非同期操作をより便利にしたが、実は彼はGenerator関数の文法糖の基本的な使い方である.
    function get1(){
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{
          resolve(1)
        },2000)      
      })
    }
    
    async function getSet(){
      const n = await get1()
      //const n = await '111'
      return n
    }
    getSet().then(console.log)
    

    説明:
  • awaitコマンドはasync関数でのみ使用でき、通常の関数で使用するとエラーが表示されます.
  • awaitの後ろには、get 1 returnから出たPromiseインスタンスなどのPromiseオブジェクトがあります.Promiseオブジェクトでない場合は、「111」を直接返すなど、対応する値を直接返します.
  • Promiseオブジェクトが、値xでfulfilledされると、値x.Promiseオブジェクトが戻り、異常eでrejectedされると、異常e
  • が投げ出す.
  • async関数が返すPromiseオブジェクトは、内部のすべてのawaitコマンドの後のPromiseオブジェクトが実行されるまで待たなければ、ステータスが変更されません.await文の後ろにあるPromiseオブジェクトがreject状態になったり、returnに遭遇したりすると、async関数全体が実行を中断します.
  • awaitはPromiseオブジェクトを待つとasync functionが実行を一時停止し、Promiseオブジェクト決議の後になってからasync functionが実行を継続する.前の非同期操作が失敗しても、後の非同期操作を中断しないでほしい場合.この場合、最初のawaitをtry...catch構造に配置することで、この非同期操作が成功するかどうかにかかわらず、2番目のawaitが実行されます.
  • async function f() {
      try {
        await Promise.reject('   ');
      } catch(e) {
    
      }
      return await Promise.resolve('hello world');
    }
    
    f().then(v => console.log(v))
    
    function getData() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          let name = '  '
          resolve(name)
        }, 100);
      });
    }
    
    async function test() {
      let newData = await getData()
      console.log(newData)
    }
    test()
    //   
    

    利点:Generator関数に比べてasync関数は次の4つの改善点があります.
  • 内蔵エフェクタ:Generator関数の実行はnext()で毎回のモジュール実行を行わなければならない.asyncはエフェクタを持参し、普通の関数と同じように呼び出すだけで
  • を実行できる.
  • のより良い意味:asyncは関数に非同期操作があり、awaitは後続の式が結果を待つ必要があることを示します.戻り値はPromise:async関数の戻り値はPromiseオブジェクトで、thenメソッドで次の操作を指定できます.さらにasync関数は複数の非同期関数の操作と見なすことができ、パッケージされたPromiseオブジェクトであり、awaitコマンドは内部thenコマンドの構文糖、すなわちPromiseである.all()の使い方複数のawaitコマンドの後の非同期操作は、リレー関係が存在しない場合は、同時にトリガーすることが望ましい.
  •     //    getFoo(), getBar()    
        
        //    
        async function getSet(){
            let [foo, bar] = await Promise.all([getFoo(), getBar()]);
            return [foo, bar]
        }
    
        //    
        async function getSet(){
            let fooPromise = getFoo();
            let barPromise = getBar();
            let foo = await fooPromise;
            let bar = await barPromise;
            return [foo, bar]
        }
    

    参考記事(侵入)