非同期/待機への旅


Async/waitはここにあります.関係なく、ウェブの大部分はまだ移行していません、そして、多くのCodeBaseは今日コールバックを使い続けます.非同期のJavaScriptをコールバックに比べて動作するように信じられないほど直感的な方法です.どのような非同期になるのかを待ってみましょう/非常に素晴らしいとどのようにコールバックからそよ風から離れて移動を行うことができます.

非同期JSの到来
初めはJavaScriptは純粋に同期していたので、コードは順番に実行できるだけだった.しばらくの間、これはコードが上から下へ順番に書かれなければならなかったことを意味しました.時間が経つにつれてJavaScriptはより複雑な操作のために使用され、このパラダイムは、発生したあらゆるアクションがメインスレッドをブロックし、“ジャンキー”ユーザーエクスペリエンスの結果として制限された.2005年のAjax/XHR運用の導入はJSに非同期能力をもたらした.これはメインスレッドをブロックすることなくサーバにリクエストを行うことを可能にしました.
async - jsでは、オールマイティコールバックの導入も行った.コールバックは、他の関数への引数として渡される単純な関数であり、返り値内に含まれる“parent”関数が実行されるときのみ実行されます.コールバックを使用すると、コードの操作順を常に確認できます.
イベントリスナーは、コールバックを使用するJavaScriptの非同期操作の一般的な例です.
document.querySelector("button").addEventLister("click", () => {
  // run a function //
});
もう一つの一般的な例はsetTimeout 関数は、特定の時間が経過した後に関数を実行できるようにします.
setTimeout(() => {
  // run some function after 5s
}, 5000);

彼らはそれをコールバック地獄と呼ぶ
コールバックは、特定の操作が完了した後に発生するアクションを宣言する優れた方法です.しかし、コールバックが悪名高いのは、要求のシーケンスを連鎖させたり、非同期関数からデータを返すようにする複雑さです.
Google APIにアクセスしようとしましょう.そのためには、OAuthを使ってリクエストを認証し、生成されたアクセストークンを使って関連APIサービスへのリクエストを行う必要があります.イベントは特定のシーケンスで発生する必要があるので、コールバックが必要です.インfetchData 関数は、Google Authインスタンス[ a ]を作成することから始めます.その後、GoogleのOAuthプロセスから生成された特定のコードを渡します.more on that here — にgetToken Authインスタンス[ b ]でアクセス可能なメソッド.一度アクセストークンがgetToken トークンを取得し、OAuthインスタンス[ C ]で設定し、最後にコールバック[ d ]を実行します.
const { google } = require("googleapis");

function fetchData(code, callback) { // a
  const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URIS } = process.env;
  const oAuth2Client = new google.auth.OAuth2(
    `${CLIENT_ID}`,
    `${CLIENT_SECRET}`,
    `${REDIRECT_URIS}`
  );
  oAuth2Client.getToken(code, (err, token) => { // b
    if (err) return console.error("Error retrieving access token", err);
    oAuth2Client.setCredentials(token); // c
    callback(oAuth2Client); // d
  });
}
これは一見してまっすぐに見えるかもしれませんがfetchData 関数は、Google APIからデータを返します.上に設定した電流は有効であるが、未定義である.これは、定義によってコールバックが値を返さないので、結果を指定してコールバックを実行するだけです.したがって、既存のコールバックフローで関数を固定することはできません.子宮筋
我々の非同期関数が値を返すことを保証する1つの方法は、それをラップしているすべての関数も非同期であることを確認することです.これは約束が便利に来るところです.

約束する
約束は“最終的に利用可能になる値のプロキシ”と定義されます.非同期の性質のため、非同期関数から返される値を扱うのに最適です.約束は一般に3つの州を持ちます.最初に呼ばれると、約束は保留中の状態で開始されます、そして、それがラップする関数から戻り値を受け取ると、それは解決するか、拒絶して、それからコールバック機能を呼び出して、それぞれキャッチします.上記の例では、settimeoutの例について説明します.
new Promise((resolve, reject) => {
  setTimeout(() => {
  // run some function after 5s
  resolve('some value')
  }, 5000);
})
私たちが作った唯一の変更は、私たちのSetTimeoutを約束でラップし、some value . 我々が上記の機能を呼ぶことになっていたならば、我々は現在、戻り値をつかむことができて、我々がそれを選ぶならば、コンソールにそれを記録することができます.約束の追加機能は、エラー処理は、より直感的です.エラーが発生する場所の明確な意味なしでコンソールにエラーをログバックする必要があるのではなく、エラーをバブリングして適切に扱うことを約束します.
function timeout() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { ... })
  })
}

timeout()
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.error(err)
  })
我々のGoogle APIに約束を適応するのは簡単です.我々がしなければならないすべては、我々の非同期機能呼び出しをラップすることですgetToken 約束で.このように、我々は全体的な機能をfetchData 実際に使用できる値を返します.
function fetchData (code, callback) {
  const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URIS } = process.env;
  oAuth2Client = new google.auth.OAuth2(
      `${CLIENT_ID}`,
      `${CLIENT_SECRET}`,
      `${REDIREC_URIS}`
  );
  return new Promise((resolve, reject) => {
      return oAuth2Client.getToken(code, (err, token) => {
        if (err) reject(err);
        oAuth2Client.setCredentials(token);
        console.log(callback(oAuth2Client));
        resolve(callback(oAuth2Client));
      })
  })
}
テストに約束をするために、我々の例に余分な複雑さを加えましょう.を参照してくださいfetchData 関数は、約束を使用する非同期操作です.我々の呼び出しcallback では、成功またはエラー状態のいずれかをキャプチャするためにコールバックを付けます.連続した非同期関数呼び出しの数が増加するので、この一回の謙虚な機能の複雑さはそうします.

function fetchData (code, callback) {
  const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URIS } = process.env;
  oAuth2Client = new google.auth.OAuth2(
      `${CLIENT_ID}`,
      `${CLIENT_SECRET}`,
      `${REDIREC_URIS}`
  );
  return new Promise((resolve, reject) => {
      return oAuth2Client.getToken(code, (err, token) => {
        if (err) reject(err);
        oAuth2Client.setCredentials(token);
        console.log(callback(oAuth2Client));
        callback(oAuth2Client)
          .then(res => { resolve(res) })
          .catch(err => { reject(err) })
      })
  })
}

非同期/待機
約束に関するノードドキュメンテーションが説明するように、Async/Waitは約束と発電機の組合せです、そして、約束の上で基本的により高いレベル抽象化です.asyncは関数を非同期として定義し、待機する外部のasync関数が待機しなければならない内部関数を宣言します.
Async/Waitはまだ約束を使用しているので、我々の以前のpromifiedsetTimeout 例.ただし、then and catch 戻り値をつかむためのコールバック、非同期/待機を使用しましょう.ここで言及する価値がありますasync 常に約束を返します.この関数をasync/waitを使うものにするには、async キーワード.
我々は一歩一歩進んで、我々の機能呼び出しをラップしますtry…catch ブロックを実行するとエラーが適切に処理できます.多田!

async function timeout() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { ... })
  })
}

try {
  const time = timeout()
  console.log(time)
} catch(err) {
  console.error(err)
}
Async/Waitを理解しているので、Google APIコードを再利用して使用しましょう.呼び出し元の関数をasyncと定義し、その中でgetOutputとsetCredentialsに対してOAuth呼び出しを待機させます.これによって、私たちのasync oauth呼び出しをpromisifyし、その中のすべての約束が解決されたか拒否されたときに主関数が返されるのを確実にすることができます.また、私たちのoauthコールをtry catch catchブロックで包んで、エラーが発生したらすぐにキャッチされます.
async function fetchData(code, callback) {
    const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URIS } = process.env;
    oAuth2Client = new google.auth.OAuth2(
      `${CLIENT_ID}`,
      `${CLIENT_SECRET}`,
      `${REDIREC_URIS}`
    );
    try {
      let token = await oAuth2Client.getToken(code);
      await oAuth2Client.setCredentials(token.tokens);
      return callback(oAuth2Client)
    } catch (e) {
      return console.error("Error retrieving access token", e);
    }
}

async/waitはここにあります
上記の例から見たように、コールバックからのAsync/waitに移行するのは難しいことではありません.async/waitと共に働くのは、コールバックからのパラダイムシフトのいくらかを必要としますが、Async/Waitがより直感的なエラー処理に関してもたらす利点と、より簡単なデバッグは、スイッチを説得力のあるものにするための引数を作ります.