async wait動作原理

30378 ワード

asyncとawaitはどのように操作しますか?
babelの試用版で変換を行い、内部の動作を理解します.
非同期関数を使用せずに、次のコードを使用してテストする準備をします.
async、waitは非同期関数だけを使用しますか?考えてみればそうではない.任意の関数にawaitを適用できます.
まず、次のコードの結果値を推定します.
function a() {console.log('a')}

async function b() {
    console.log('b1')
    await a()
    console.log('b2')
}
b()
console.log('c')
実際、上記の結果値を予測できれば、次の文章を見る必要はありません.
内部の原理が気になる人を見ればいいです.
上のコードをバーベルに変換すると、
上のコードをバーベルから回すと、次のコードが表示されます.
惨めで見るに忍びない
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}

function _asyncToGenerator(fn) {
  return function () {
    var self = this,
      args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      _next(undefined);
    });
  };
}

function a() {
  console.log('a');
}

function b() {
  return _b.apply(this, arguments);
}

function _b() {
  _b = _asyncToGenerator(function* () {
    console.log('b1');
    yield a();
    console.log('b2');
  });
  return _b.apply(this, arguments);
}

b();
console.log('c');
もっと簡単に
私たちにとって、処理ミスの部分は見えません.コードをより簡単に変更してから確認しましょう.
かつてasyncだったb関数はbとbに分けられ,待機していた部分はjenerator関数となり,内部から完成品に変わったことがわかる.これしかないか確認してから、コードを下に見てみましょう.
function asyncGeneratorStep(gen, resolve, _next, key, arg) {
  /**
   * 제네레이터 객체의 next 메소드를 호출한다.
   */
  const genValue = gen[key](arg);

  /**
   * generator가 done이 됐으면 resolve한다.
   * 아니면 뒤의 작업은 then으로 이어서 한다. => 여기서 마이크로 테스크 큐로 들어가기 때문에 순서의 차이가 발생한다.
   */
  if (genValue.done) {
    resolve(genValue.value);
  } else {
    Promise.resolve(genValue.value).then(_next);
  }
}

function _asyncToGenerator(fn) {
  return function () {
    const self = this;
    const args = arguments;

    return new Promise((resolve) => {
      // gen: 제네레이터 객체
      const gen = fn.apply(self, args);
      const _next = (value) => {
        asyncGeneratorStep(gen, resolve, _next, 'next', value);
      };

      // 첫번째 await까지 실행한다.
      _next(undefined);
    });
  };
}

function a() {
  console.log('a');
}

function b() {
  return _b.apply(this, arguments);
}

function _b() {
  function* asyncFunction() {
    console.log('b1');
    yield a();
    console.log('b2');
  }

  _b = _asyncToGenerator(asyncFunction);
  return _b.apply(this, arguments);
}

b();
console.log('c');
コードの理解_asyncToGeneratorおよびasyncGeneratorStepは、上記のコードから理解される._asyncToGeneratorの内部ではasyncGeneratorStepが使用される.
_asyncToGenerator
function _asyncToGenerator(fn) {
  return function () {
    const self = this;
    const args = arguments;

    return new Promise((resolve) => {
      const gen = fn.apply(self, args);
      const _next = (value) => {
        asyncGeneratorStep(gen, resolve, _next, 'next', value);
      };

      _next(undefined);
    });
  };
}
  • 関数はasyncToGeneratorと呼ばれ、generator関数に変更されたasync関数をgeneratorオブジェクトに変更して実行する関数です.パラメータとしてgenerator関数を受け入れます.
  • Promiseオブジェクトの関数を返します.
  • どのPromiseオブジェクトを返しますか?
    new Promise((resolve) => {
          // gen: 제네레이터 객체
          const gen = fn.apply(self, args);
          const _next = (value) => {
            asyncGeneratorStep(gen, resolve, _next, 'next', value);
          };
    
          // 첫번째 yield(await)까지 실행한다.
          _next(undefined);
        });
  • パラメータで受信したジェネレータ関数を使用してジェネレータオブジェクトを作成します.(gen)
  • next関数を実行します.==asyncGeneratorStep関数を実行します.
  • 最終的に、asyncToGenerator関数は、パラメータとして受信されたGenerator関数をnextとすることができる.つまり,1回目の待ちを実行すればよい.
    next関数asyncGeneratorStepについて説明します.
    asyncGeneratorStep
    人の字から一つ一つ調べましょう.
  • gen:JENNERオブジェクト
  • 解析:承諾された解析関数
  • next:asyncToGeneratorのnext関数(再帰的に使用可能).
  • キー、arg:arg:1つのキーとパラメータ値arg(例ではthrowがないと仮定)、jeneratorのnextまたはthrow(throwがないと仮定)を実行するために使用されます.
  • の最初のasyncToGeneratorで実行されるasyncGeneratorStepでは、genValueがgen(サードパーティオブジェクト)のnextメソッドを呼び出します.(最初の待機が実行されます.)
  • 今genValueは終わりましたかgenValuedoneと確認して完了した場合、解析が完了していない場合は、後続のタスクをthereに渡して実行します.(つまり、最初の待機後のタスクが入って実行されます.)
  • ビット目の操作を繰り返します.
  • function asyncGeneratorStep(gen, resolve, _next, key, arg) {
      /**
       * 제네레이터 객체의 next 메소드를 호출한다.
       */
      const genValue = gen[key](arg);
    
      /**
       * generator가 done이 됐으면 resolve한다.
       * 아니면 뒤의 작업은 then으로 이어서 한다. => 여기서 마이크로 테스크 큐로 들어가기 때문에 순서의 차이가 발생한다.
       */
      if (genValue.done) {
        resolve(genValue.value);
      } else {
        Promise.resolve(genValue.value).then(_next);
      }
    }
    
    asysn wait原理
    async,awaiの原理はここに隠されている.
    async関数はサードパーティ関数に変更されて実行されます.
    async関数で変更したjner layer関数が内部で生成されると、後の作業がthenに渡されます.
    これは、後の操作がマイクロタスクキューに入り、呼び出しスタックが空になり、実行されるため、上部の例と同様の現象が発生するためです.
    結論!
  • async,await関数
  • サードパーティ関数の実行時に、生産量を基準として、承諾から方法に移行します.
  • このとき、アクセスメソッドの後部はマイクロタスクキューに入り、コールスタックが空になった後、実行を継続する.
  • の最後の部分
    asyncToGenerator、asyncGeneratorStep関数の原理を理解し、babelでtransformileコードを初めて見るとわかります.
    結局、待っていた秘密はジェナレットとプロミスだった.
    この現象は反応の中でも予想外の結果を生むので、次のヒントで理解してみましょう.
    ref
  • https://meetup.toast.com/posts/73
  • https://medium.com/sjk5766/async-await-原理-cc 643 f 18526 d
  • https://ko.javascript.info/microtask-queue