6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年2月25日 非同期処理:コールバック/Promise/Async - Promise APIとAsync Functionを組み合わせる (JavaScript Primer)

昨日の朝会ではawait式でリソースを順番に1つずつ取得していましたが、Promise.allを使ってまとめて取得する例から見ていきます 。

Promise APIとAsync Functionを組み合わせる

function dummyFetch(path) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (path.startsWith("/resource")) {
                resolve({ body: `Response body of ${path}` });
            } else {
                reject(new Error("NOT FOUND"));
            }
        }, 1000 * Math.random());
    });
}
// 複数のリソースをまとめて取得する
async function fetchAllResources(resources) {
    // リソースを同時に取得する
    const promises = resources.map(resource => {
        return dummyFetch(resource);
    });
    // すべてのリソースが取得できるまで待つ
    // Promise.allは [ResponseA, ResponseB] のように結果が配列となる
    const responses = await Promise.all(promises);
    // 取得した結果からレスポンスのボディだけを取り出す
    return responses.map(response => {
        return response.body;
    });
}
const resources = [
    "/resource/A",
    "/resource/B"
];
// リソースを取得して出力する
fetchAllResources(resources).then((results) => {
    console.log(results); // => ["Response body of /resource/A", "Response body of /resource/B"]
});

上記のコードのポイント

  • Promise.allメソッドは引数で渡した全ての要素をPromiseインスタンス化し配列で返します。(PromiseState内部プロパティは一つでもrejectedならrejected、全てがfulfieldならfulfieldになります。)
  • mapメソッドは配列を受け取り、仮引数に要素を一つずつ渡してブロック内の処理を繰り返し実行します。
  • mapメソッドは非破壊的メソッドであるため新しい配列を生成した後も元の配列の値も元の変数内に残っています。
  • mapメソッドのコールバック関数は引数が一つなので引数に使用する()を省略できます。(primerでは省略していませんでしたがここではあえて省略しました。)

thisが問題となる2つのパターン

問題1: thisを含むメソッドを変数に代入した場合

thisを参照するメソッドを、変数に代入して関数として呼び出すと、ベースオブジェクトの所属先が変わっているためundefinedになってしまう。

"use strict";
const person = {
    fullName: "Brendan Eich",
    sayName: function() {
        return this.fullName;
    }
};
console.log(person.sayName()); // => "Brendan Eich"
const say = person.sayName;

say(); // => TypeError: Cannot read property 'fullName' of undefined
  • thisを使ったメソッドは変数に代入するべきではありません。
  • どうしても代入する場合はcall apply bindメソッドを使って対応すると良いです。

問題2: コールバック関数とthis

  • functionキーワードを使用してしまうとundefinedになります。
  • Arrow Functionで書けばスコープチェーンの仕組みと同様に外側のスコープを探索するので問題なく使えます。
    (Arrow Functionはthisを持たない)

復習

スコープチェーンの仕組み

  • 今いるスコープを探索してなかったら外側のスコープに探索しにいく仕組みの事です。

静的スコープ

  • 定義された位置でスコープが決まります。
  • 呼び出し元によって参照先が変わりません。
  • ある変数がどの値を参照するかは静的に決まります。

メモリ管理の仕組み

参考

非同期処理:コールバック/Promise/Async - Promise APIとAsync Functionを組み合わせる (JavaScript Primer)