Выполните задачу перед выполнением цикла для следующего члена - PullRequest
0 голосов
/ 25 января 2019

У меня есть этот простой и короткий код JavaScript для веб-сайта:

db.collection("classes").doc(data.readGrades).get().then(function(doc) {
          if (doc.exists) {
            const data = doc.data();
            const members = data.members;

            members.forEach(el => {

              db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
                querySnapshot.forEach(function(doc) {
                    const data = doc.data();

                    //some stuff

                });
              });
            })

          } else {
            console.log("No such document!");
          }
        }).catch(function(error) {
          console.log("Error getting document:", error);
        });  

Как видите, мне нужно запустить две задачи Firebase Firestore. Важно, чтобы сначала это выполнялось db.collection("classes").doc(data.readGrades).get().then(function(doc) {, и для каждого из членов эта задача следует за db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) { (ПРИМЕЧАНИЕ. Вторая задача требует первого из-за переменной el). Поэтому моя проблема в том, что я должен дождаться завершения второго задания, прежде чем цикл для следующего члена может продолжаться.

И это точно мой вопрос: как выполнить второе задание перед запуском цикла для следующего члена?

EDIT:
Я изменил свой код Javascript следующим образом:

db.collection("classes").doc(data.readGrades).get().then(function(doc) {
    if (doc.exists) {
        const data = doc.data();
        const members = data.members;

        members.reduce((chain, el) => {

          table_number++;
          const html = fillTemplate("grade_table" + table_number, member_name);
          document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);

            return chain.then(() =>
                db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
                    querySnapshot.forEach(function(doc) {
                        const data = doc.data();

                        addToTable("grade_table" + table_number, doc.id, data.mdl, data.klu);
                    });
                })
            )
        }, Promise.resolve());
    } else {
        console.log("No such document!");
    }
}).catch(function(error) {
    console.log("Error getting document:", error);
});

function fillTemplate(table_name, id) {
  console.log("ID OF TABLES  " + table_name);
  return `
  <div class="noten_tabelle_permission" id="noten_tabelle_permission">
    <h1 id="member_name">${id}</h1>
    <table id="${table_name}" style="width:100%">
      <tr>
        <th>Fach</th>
        <th>mündlich</th>
        <th>Klausur</th>
      </tr>
      <!-- Make content with js code -->
    </table>
  </div>
  `;
}

function addToTable(table_name, subject, mdl, klu) {

  var subject_name = getSubjectByNumber(subject);
  var short_subject = getSubjectShortByNumber(subject);

      //Zeile erstellen

      console.log("addToTable " + table_name);

      var y = document.createElement([short_subject]);
      y.setAttribute("id", [short_subject]);
      document.getElementById([table_name]).appendChild(y);

      //Spalten in einer Zeile

      var y = document.createElement("TR");
      y.setAttribute("id", [short_subject]);

      //Spalten in einer Zeile

      var cE = document.createElement("TD");
      var tE = document.createTextNode([subject_name]);
      cE.appendChild(tE);
      y.appendChild(cE);

      var a = document.createElement("TD");
      var b = document.createTextNode([mdl]);
      a.appendChild(b);
      y.appendChild(a);

      var c = document.createElement("TD");
      var d = document.createTextNode([klu]);
      c.appendChild(d);
      y.appendChild(c);


      document.getElementById(table_name).appendChild(y);
}

Так что обещание работает. Но теперь у меня проблема в том, что обещание работает неправильно. Как видите, я хочу запустить задачу Firestore (Database) для каждого участника. Так что обещание должно уволить каждого нового члена. Но обещание сработает первым в конце.

Базовые знания: я получаю членов из ArrayList из базы данных, и для каждого отдельного члена мне нужно получить конкретные значения на основе этого члена. Когда значения для члена загружены, я добавляю их в таблицу, которая заполняется для каждого члена (как вы можете видеть) в предыдущей задаче. Как я уже сказал, обещание сработает в конце. Таким образом, значения не будут добавлены в конкретную таблицу члена. Они будут добавлены в последнюю таблицу.

РЕДАКТИРОВАТЬ 2 : Это отредактированный код:

members.reduce((chain, el) => {

              db.collection("users").doc(el).collection("user_data").doc("u").get().then(function (doc) {
                const data = doc.data();
                var member_name = data.name;

                table_number++;
                const html = fillTemplate("grade_table" + table_number, member_name);
                document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
              })


          return chain.then(((table_num) => {
            return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
              querySnapshot.forEach(function (doc) {
                const data = doc.data();

                addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);

              });
            })
          }).bind(null, table_number))
        }, Promise.resolve());

Итак, проблема в том, что задача, в которой я получаю имя_символа, выполняется асинхронно, а другая часть - нет. Так что я могу выполнить всю задачу только тогда, когда все загружено. Потому что, как вы видите, мне нужно также имя_члена. ТАК, как перекодировать его, чтобы задача запускалась, когда загружается только все (имя участника / оценки).

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Другой подход заключается в использовании Promise.all() следующим образом:

db.collection("classes").doc(data.readGrades).get()
.then(doc => {
          if (doc.exists) {
            const data = doc.data();
            const members = data.members;

            const promises = []

            members.forEach(el => {
              const usersPromise = db.collection("users").doc(el).collection("grades").get();
              promises.push(usersPromise);
            });

            return Promise.all(promises);

          } else {
            console.log("No such document!");
            throw new Error("No such document!");
          }
})
.then(results => {
    results.forEach(querySnapshot => {
        querySnapshot.forEach(doc => {
                const data = doc.data();
                    //some stuff
        });
    });     
})  
.catch(error => {
    console.log("Error", error);
});

results, возвращаемое, когда единичное обещание, возвращаемое Promise.all(promises), разрешает, является массивом со всемирезультаты обещаний, в том же порядке, в котором вы выдвинули обещания в массиве promises .

0 голосов
/ 25 января 2019

Вы можете использовать reduce вместо forEach с пустым Promise, переданным в качестве начального значения для последовательной обработки членов:

db.collection("classes").doc(data.readGrades).get().then(function(doc) {
    if (doc.exists) {
        const data = doc.data();
        const members = data.members;

        members.reduce((chain, el) => {
            return chain.then(() =>
                db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
                    querySnapshot.forEach(function(doc) {
                        const data = doc.data();
                        //some stuff
                    });
                })
            )
        }, Promise.resolve());
    } else {
        console.log("No such document!");
    }
}).catch(function(error) {
    console.log("Error getting document:", error);
});

Обновление # 1:

Принимая во внимание обновленный вопрос, данные, вероятно, добавляются в последнюю таблицу, поскольку за время разрешения обещаний все итерации сокращения были завершены (и, следовательно, переменная table_name сохраняет значение изпоследняя итерация).Вы можете решить это с помощью значения binding table_name для обратного вызова, вызванного при разрешении обещания:

members.reduce((chain, el) => {

  table_number++;
  const html = fillTemplate("grade_table" + table_number, doc.id);
  document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);

  return chain.then(((table_num) => {
    return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
      querySnapshot.forEach(function (doc) {
        const data = doc.data();

        addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);
      });
    })
  }).bind(null, table_number))
}, Promise.resolve());

Обновление # 2 : для выполнения нескольких асинхронных операцийвпоследствии в течение одной итерации сокращения попытайтесь объединить их в цепочку, используя метод обещания then:

members.reduce((chain, el) => {

  return chain
    .then(() => db.collection("users").doc(el).collection("user_data").doc("u").get())
    .then(doc => {
      const data = doc.data();
      var member_name = data.name;

      table_number++;
      const html = fillTemplate("grade_table" + table_number, member_name);
      document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
      return table_number;
    })
    .then(table_num => {
      return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
        querySnapshot.forEach(function (doc) {
          const data = doc.data();

          addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);

        });
      })
    })

}, Promise.resolve());
...