音ゲー、fps、DTM、プログラミング雑記置き場

ブログタイトル通りに雑記を垂れ流す。

【JavaScript】async,awaitでもcallbackは必要

コールバック関数とは

// A
function hello(name) {
  alert(`Hello, ${name}`);
}

// B
function processUserInput(callback) {
  const name = prompt("Please enter your name.");
  callback(name);
}

// C
processUserInput(hello);
  • 上記Cブロックのソースでcallback = helloを行い、
    callbackオブジェクトにhelloメソッドの参照情報が格納される
    • C#のデリゲート、Cの関数ポインタに近い動き
      • 下記ソースのようにgoodbyeメソッドを追加し
        processUserInput(goodbye);とすれば、
        アラートで出力されるのはDブロックのソース内容となる
// D
function goodbye(name) {
  alert(`Goodbye, ${name}`);
}
  • 以下の動きからコールバック関数は自身が何か実行しているわけではなく
    引数で受け取ったメソッドの情報を、展開先の処理内に横流ししているだけ。
  • なので少し話はそれるが、コールバックを引数に持つブロックBのメソッドは
    多数のイベントハンドラを受付け、発火させるという実装でもできそう

コールバックを使用する例:非同期処理

  • 上記のA-Dブロックを非同期で実行させたいとなるとコールバックのネストか
    async,awaitのどちらか
    • 前者は非同期の処理数に応じてネストが入れ子になるコールバック地獄になるので後者で実装する
function hello(name) {
  return new Promise(resolve => {
    //Promiseの第一引数は自身の正常終了時の処理を記載
    alert(`Hello, ${name}`);
    //自身のPromiseステータスをresolveにし正常終了時の結果を返す
    resolve(name);
  })
}

//コールバックのみの実装だとここがhelloの中にネストになる
function goodbye(name) {
  return new Promise(resolve => {
    alert(`Goodbye, ${name}`);
    resolve(name);
  })
}

async function processUserInput(callback) {
  const name = prompt("Please enter your name.");
  //helloが正常終了(resolve)したらコールバック引数を次のPromiseに渡す
  return await hello(name).then(goodbye);
}

processUserInput();
  • goodbyeメソッドを追加したことによって非同期処理が必要になるようにした
    • processUserInputメソッドを下記のように書くと同期処理は担保されない
hello(name)
goodbye(name);
  • async,awaitによりコールバックのネストは回避されたが、
    Promiseクラス、thenメソッドの引数にはコールバックは使用されている
    • 立ち位置は同じで受け取ったメソッドの情報の参照をアロー関数以降の処理に横流ししているだけ
      • thenではアロー関数自体を省略していけど

まとめ

  • async,awaitでもコールバック関数は使用している
  • あくまでコールバックの「ネスト」が解決するだけ