1枚のDEEP DIVEサマリー(サードパーティasync/await)


ジェニーン・レイトとasync/await


どうして勉強しますか。


実際、async waitがGeneratorに由来していることさえ知らなかった.既存のシングルダイジェスト(Premis)でも言っていますが、async awaitについて少し知っているのはPromise文法が難しいからだけです
.then()
.catch()
.finally()
このようなpromisのプロトタイプ手法は「どこで使えるのか、どこで使えるのか」も分からず、後の内容ですが、エラー処理のためにtryでcatch非同期関数を囲むようにしても、正しいエラーキャプチャにはなりません.
したがって、この章でプロセスベースasync/awaitのさまざまな使用方法を学習すると、非同期処理と非同期コード内部の同期処理プロセスをより深く理解できます.

ジェニーン・レイトとは?


ES 6が導入したジェネレータは,コードブロックの動作を一時停止し,必要に応じて回復できる特殊な関数である.サードパーティと一般的な関数の違いは次のとおりです.

①サードパーティ関数は、関数呼び出し側に関数実行の制御権を譲渡することができる。


関数呼び出し者は、関数の実行を一時停止または再起動できます.これは、関数の制御権が関数呼び出し元に譲渡され、関数によって独占されるのではないことを意味する.

②サードパーティ関数は、関数呼び出し者と関数の状態を交換することができる。


関数は、ステータスを関数呼び出し元に渡すか、関数呼び出し元からステータスを受信することができます.

③サードパーティ関数を呼び出してサードパーティオブジェクトを返します。


jner layer関数を呼び出すと、関数コードを実行するのではなく、小さなバージョンのjner layerオブジェクトが返されます.

サードパーティ関数の定義


サードパーティ関数はfunction*キーワードとして宣言されます.次に、1つ以上の降伏式が含まれます.それ以外は、通常の関数を定義する方法と同じです.
// 제너레이터 함수 선언문
function* genDecFunc() {
  yield 1;
}

// 제너레이터 함수 표현식
const genExpFunc = function* () {
  yield 1;
};

// 제너레이터 메서드
const obj = {
  *genObjMethod() {
    yield 1;
  },
};

// 제너레이터 클래스 메서드
class MyClass {
  *genClsMethod() {
    yield 1;
  }
}
関数キーと関数名の間の任意の場所で、別名(*)を見つけることができます.しかし、一貫性を保つためにはfunctionキーワードに続くことをお勧めします.
function* genFunc() { yield 1; }

function * genFunc() { yield 1; }

function *genFunc() { yield 1; }

function*genFunc() { yield 1; }
アクセラレータ関数は矢印関数として定義できません.
const genArrowFunc = * () => {
  yield 1;
}; // SyntaxError: Unexpected token '*'
new演算子とともにコンストラクション関数を使用してjner rator関数を呼び出すことはできません.
function* genFunc() {
  yield 1;
}

new genFunc(); // TypeError: genFunc is not a constructor

マネージャオブジェクト


jner rator関数を呼び出すと、通常の関数のように関数コードブロックを実行するのではなく、jner ratorオブジェクトが生成され、返されます.JENNERが返すJENNERオブジェクトは反復可能であり、反復的でもある.
すなわち、サードパーティオブジェクトはSymbolである.これはiteratorメソッドのイテレーションを継承し、nextメソッドのイテレーションを持ち、valueとdone Propertyのイテレーションツリー・セクション・オブジェクトを返します.
JENNERオブジェクトはnextメソッドを持つイテレーションなのでSymbol.iteratorメソッドを呼び出してウィジェットを個別に作成する必要はありません.
// 제너레이터 함수
function* genFunc() {
  yield 1;
  yield 2;
  yield 3;
}

// 제너레이터 함수를 호출하면 제너레이터 객체를 반환한다.
const generator = genFunc();

// 제너레이터 객체는 이터러블이면서 동시에 이터레이터다.
// 이터러블은 Symbol.iterator 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체다.
console.log(Symbol.iterator in generator); // true
// 이터레이터는 next 메서드를 갖는다.
console.log("next" in generator); // true
JENNER RATERオブジェクトはnextメソッドを持つイテレーションですが、イテレーションには①return②throwメソッドはありません.サードパーティ製オブジェクトの3つのメソッドを呼び出す場合は、次の操作を行います.
  • nextメソッドを呼び出すと、サードパーティ関数の最終品目式までコードブロックが実行され、イテレーションオブジェクトが返されます.{ value : ***, done : boolean}
  • 戻りメソッドが呼び出されると、パラメータで渡された値を値property値とし、trueをdone property値とするイテレーションでオブジェクトが切り捨てられます.{value:「引数」,don:true}
  • function* genFunc() {
      try {
        yield 1;
        yield 2;
        yield 3;
      } catch (e) {
        console.error(e);
      }
    }
    
    const generator = genFunc();
    
    console.log(generator.next()); // {value: 1, done: false}
    console.log(generator.return("End!")); // {value: "End!", done: true}
  • throwメソッドを呼び出すと、入力パラメータのエラーが発生し、値として定義されていないproperty値を返し、trueをdone property値とするイテレーションでオブジェクトを切断します.{ value: undefined, done : true}
  • function* genFunc() {
      try {
        yield 1;
        yield 2;
        yield 3;
      } catch (e) {
        console.error(e);
      }
    }
    
    const generator = genFunc();
    
    console.log(generator.next()); // {value: 1, done: false}
    console.log(generator.throw("Error!")); // {value: undefined, done: true}

    マネージャの一時停止と続行


    Jenner ratorは、1 yieldキーワードと2 nextメソッドで実行を一時停止し、必要に応じて再開することができます.
    一般的な関数は、呼び出し後に制御権がこの関数によって独占されますが、サードパーティは、必要に応じて関数の実行を再開するために、制御権を関数呼び出しに譲渡(降伏)することができます.
    nextメソッドを使用してジェネレータを実行すると、コードブロックで降伏キーワードの後の式の計算結果がジェネレータ関数呼び出し者に返され、ツリー型オブジェクトとして返されます.
    { value : , done : }
    // 제너레이터 함수
    function* genFunc() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    // 제너레이터 함수를 호출하면 제너레이터 객체를 반환한다.
    // 이터러블이면서 동시에 이터레이터인 제너레이터 객체는 next 메서드를 갖는다.
    const generator = genFunc();
    
    console.log(generator.next()); // {value: 1, done: false}
    
    console.log(generator.next()); // {value: 2, done: false}
    
    console.log(generator.next()); // {value: 3, done: false}
    
    console.log(generator.next()); // {value: undefined, done: true}
    サードパーティ製オブジェクトのnextメソッドが呼び出されると、最終品目式が実行され、一時停止されます.このとき、関数の制御権は呼び出し元(完成品)に再譲渡されます.後で必要に応じて、出資者がnextメソッドを再度呼び出すと、一時停止したコードから実行され、次の完成品式に実行され、再び一時停止されます.
    トリミングオブジェクト{value:,don:}を返し、最終品目式が最後まで実行された場合、valueはundefinedを返し、doneはtrueトリミングオブジェクトを返して終了します.

    async/await


    上記の方法では、同期のようにマネージャを使用して非同期処理を実現できます.しかしコードは非常に冗長になり、可読性も悪くなった.
    ES 8にはasync/awaitが導入されており,同期処理のように非同期処理が簡単で読み取り可能である.
    async/awaitはpromisに基づいて動作します.
    async/awaitを使用すると、同期処理のようにPromisのther/catch/finallyなどの後続処理方法にコールバック関数を渡すことができます.
    すなわち,プロセスの後続処理方法がなく,同期処理のようにプロセス戻り処理結果を実現できる.
    <body>
      <pre></pre>
      <script>
        async function fetchTodo() {
          const url = "https://jsonplaceholder.typicode.com/todos/1";
    
          const response = await fetch(url);
          const todo = await response.json();
          console.log(todo);
          const result = JSON.stringify(todo, null, 2);
          document.querySelector("pre").innerHTML = result;
          // {userId: 1, id: 1, title: 'delectus aut autem', completed: false}
        }
        fetchTodo();
      </script>
    </body>

    async関数


    awaitキーワードはasync関数の内部で使用する必要があります.
    async関数はasyncキーワード定義を使用し、常にpromisを返します.
    async関数は、async関数が明示的にプロセスを返さない場合でも、デフォルトの解析戻り値のプロセスを返します.
    // async 함수 선언문
    async function foo(n) { return n; }
    foo(1).then(v => console.log(v)); // 1
    
    // async 함수 표현식
    const bar = async function (n) { return n; };
    bar(2).then(v => console.log(v)); // 2
    
    // async 화살표 함수
    const baz = async n => n;
    baz(3).then(v => console.log(v)); // 3
    
    // async 메서드
    const obj = {
      async foo(n) { return n; }
    };
    obj.foo(4).then(v => console.log(v)); // 4
    
    // async 클래스 메서드
    class MyClass {
      async bar(n) { return n; }
    }
    const myClass = new MyClass();
    myClass.bar(5).then(v => console.log(v)); // 5
    クラスのコンストラクション関数(コンストラクション関数)メソッドはasyncメソッドではありません.クラスのコンストラクション関数はインスタンスを返さなければなりませんが、async関数は常にプロセスを返さなければなりません.
    class MyClass {
      async constructor() { }
      // SyntaxError: Class constructor may not be an async method
    }
    
    const myClass = new MyClass();

    awaitキーワード


    awaitキーワードは、プロセスが固定状態(非同期処理を実行する状態)に達するのを待ち、固定状態に達するとプロセス解析の処理結果を返します.awaitキーワードはfromisの前で使用する必要があります.
    <body>
      <pre></pre>
      <script>
        const getGithubUserName = async (id) => {
          const res = await fetch(`https://api.github.com/users/${id}`); // ①
          const { name } = await res.json(); // ②
          name
            ? (document.querySelector(
                "pre"
              ).innerHTML = `찾은 이름은 ${name}입니다 😁`)
            : (document.querySelector("pre").innerHTML =
                "존재하지 않는 사용자입니다 🥲");
        };
    
        const result = window.prompt("찾고 싶은 사용자의 id를 입력하세요");
        if (result) getGithubUserName(result);
      </script>
    </body>

    waitキーワードは、プロミスが安定するのを待っていると言っています.このため、サーバが①のfetch関数に対して実行するHTTP要求に対する応答は、fetch関数が返すプロセスが一定になるまで、①が待機する.その後、一定の状態にある場合、プロセスによって解析された処理結果はres変数に割り当てられる.
    これによりawaitキーワードは次の実行を一時停止し、プロセスが安定している場合に再起動します.
    async function foo() {
      const a = await new Promise((resolve) => setTimeout(() => resolve(1), 3000));
      const b = await new Promise((resolve) => setTimeout(() => resolve(2), 2000));
      const c = await new Promise((resolve) => setTimeout(() => resolve(3), 1000));
    
      console.log([a, b, c]); // [1, 2, 3]
    }
    
    foo(); // 약 6초 소요된다.
    注意すべてのpromisにawaitキーワードを使用します.上記の例のfoo関数は、呼び出しから終了まで約6秒かかります.waitキーワードを使用すると、各プロセスの状態が安定するのを待つからです.
    これが意図的であれば構わないが、3つの非同期処理は独立して相互に関連しない非同期処理であるため、前の非同期処理が完了してから順次処理を行う必要はない.
    async function foo() {
      const res = await Promise.all([
        new Promise((resolve) => setTimeout(() => resolve(1), 3000)),
        new Promise((resolve) => setTimeout(() => resolve(2), 2000)),
        new Promise((resolve) => setTimeout(() => resolve(3), 1000)),
      ]);
    
      console.log(res); // [1, 2, 3]
    }
    
    foo(); // 약 3초 소요된다.

    エラー処理


    非同期処理に用いるコールバックモードの欠点の1つは、エラー処理が困難であることである.エラーは呼び出し元(caller)方向に伝播します.すなわち、呼び出しスタックの次の方向(実行中の実行コンテキストのプッシュ前の実行コンテキスト方向)に伝播します.しかし、非同期関数を呼び出すコールバック関数は非同期関数ではないのでtry...catch文を使用してエラーをキャプチャできません.
    try {
      setTimeout(() => { throw new Error('Error!'); }, 1000);
    } catch (e) {
      // 에러를 캐치하지 못한다
      console.error('캐치한 에러', e);
    }
    async/awaitでエラー処理はtry...catch文を使用できます.コールバック関数をパラメータとして渡す非同期関数とは異なり、プロセスを返す非同期関数は明示的に呼び出されるため、呼び出し元は明確である.
    <body>
      <pre></pre>
      <script>
        const getGithubUserName = async (id) => {
          try {
            const res = await fetch(`https://api.github.com/users/${id}`); // ①
            const { name } = await res.json();
            name ? document.querySelector('pre').innerHTML= `찾은 이름은 ${name}입니다 😁` : document.querySelector('pre').innerHTML = '존재하지 않는 사용자입니다 🥲'};
          } catch(err){
            console.error(err)
          }
    
        const result = window.prompt('찾고 싶은 사용자의 id를 입력하세요');
        if(result)getGithubUserName(result);
      </script>
    </body>
    上記の例の関数のcatch文は、HTTP通信で発生したネットワークエラーだけでなく、tryコードブロック内のすべての文で発生した通常のエラーもキャプチャできます.