非同期処理のPromiseでfor文を複数回す

03:17・2021/10/16 公開

03:17・2021/10/16 更新

という少々厄介な場面に遭遇したので記事を書いておく。

めちゃくちゃハマった経緯

vue.jsでfirestoreからデータを取ってくる際にdata属性への反映が遅く、最初に開いたときに表示されないということで4日間くらいハマりました。
コンポーネントの読み込みを遅延させたり、データを取ってくる場所を変えたり、wachやcomputedで監視してみたり色々しましたが全滅。
その頃にLINEで「データを取ってくるのが間に合ってないのでは」と指摘されて改めてコンソールで取得順を表示すると、たしかに間に合ってない(当たり前体操)。 
promise.allとか使いなよ」とありがたい教示をいただき、私は解決への糸口を掴む───────

参考記事

Promiseとthenのメソッドチェーン(直列・並列・値の受け取り・引数) – Qiita
複数の値の受け取り方法を知らなくてまたハマりかけたので注意。

Promiseの直列処理をループする(完了後に実行したい処理もある) – Qiita
最終的にコードはだいぶ違う形で活用しました。

Promiseってなんぞや編

setTimeoutはダサいぞ。JavaScript Promiseを使って処理を順番に実行しよう – 株式会社LIG
多分これが一番わかりやすい

JavaScriptのPromise – Qiita
promiseはいろんなことに使えるらしい

Promise.all() – MDN
お馴染みのMDN

今回書いたコード

vue.jsの子コンポーネント内に記述しました。
(HOMEに表示する情報の一部です)

import store from "./../../stores";//vuexの読み込み
import { doc, getDoc, collection, getFirestore } from "firebase/firestore";
const db = getFirestore();//firestoreに接続

export default {
  data() {
    return {
      rooms: "",//ここにデータを入れたい
    };
  },
  created() {
    let uid = store.getters.user.uid;//ユーザーID
    console.log("getrooms");
    let rooms_sub = [];//とりあえずこれにデータを入れる
    let myPromise1 = Promise.resolve();
    let myPromise2 = Promise.resolve();
    let getroomID = new Promise((resolve, reject) => {
      getDoc(doc(db, "users", uid)).then((docRef) => {
        resolve(docRef.data().rooms);//持ってる部屋の一覧を取得
      });
    });
    getroomID.then((roomId) => {
      for (var i = 0; i < roomId.length; i++) {
        myPromise1 = myPromise1.then(getrooms.bind(this, i, roomId));
      }//部屋の数だけ部屋情報を取得
      myPromise1.then((value) => {
        let i = value[0];
        let rooms_sub = value[1];
        let members_sub = value[2];
        for (var j = 0; j < members_sub.length; j++) {
          myPromise2 = myPromise2.then(
            getmembers.bind(this, i, j, rooms_sub, members_sub)
          );
        }//部屋ごとのメンバー情報を取得
        myPromise2.then((rooms_sub) => {
          store.commit("setRooms", rooms_sub);//vuexにセット
          console.log(rooms_sub);
          this.rooms = rooms_sub;//dataに代入
        });
      });
    });

    function getrooms(i, roomId) {//部屋情報をとってくる
      return new Promise(function (resolve, reject) {
        let docRef = doc(db, "rooms", roomId[i]);
        getDoc(docRef).then((docRef) => {
          rooms_sub[i] = {
            id: roomId[i],
            icon: docRef.data().icon,
            name: docRef.data().roomname,
            post: docRef.data().post,
            member: docRef.data().users.length,
            update: docRef.data().update,
            members: [],
          };
          resolve([i, rooms_sub, docRef.data().users]);
          console.log(i, roomId);
        });
      });
    }
    function getmembers(i, j, rooms_sub, members_sub) {//部屋のメンバー情報をとってくる
      return new Promise(function (resolve, reject) {
        let docRef2 = doc(db, "users", members_sub[j]);
        getDoc(docRef2).then((docRef) => {
          rooms_sub[i].members.push({
            id: members_sub[j],
            name: docRef.data().name,
            icon: "",
          });
          resolve(rooms_sub);
        });
      });
    }
  },
};
</script>