1

I have some items in firestore.I want to show them sequentially but in random serial. So, I did the following code. Here I want to show a specific question and then when "next" button is clicked I want to show the next item. Here, in the forEach loop, I am rendering items to the renderQuestion function. And they are showing them in frontend using HTML and CSS.

But I am fetching this issue, where they keep showing everything till the end of the loop. They don't wait for the "next" button to be pressed. So, how can I fix this? I am new to JS, please forgive me, if I am already doing something wrong.

renderQuestion = (qID, roomID) => {
  q.innerHTML = qID.data().question;
  ans1.innerHTML = qID.data().a;
  ans2.innerHTML = qID.data().b;
  ans3.innerHTML = qID.data().c;
  ans4.innerHTML = qID.data().d;
  /* next.addEventListener("click", (e) => { //not working
    e.preventDefault();
    console.log("done");
  }); */
};

runningExam = async (review) => {
  const totalQ = review.data().total_questions;
  let arr = [];
  for (let i = 0; i <= totalQ; i++) {
    arr.push(false);
  }
  let n = totalQ;

  while (n) {
    let randomNumber = getRandom(totalQ);
    if (!arr[randomNumber]) {
      arr[randomNumber] = true;
      await db
        .collection("examrooms")
        .doc(review.id)
        .collection("questions")
        .where("serial", "==", randomNumber)
        .get()
        .then((snapshot) => {
          snapshot.docs.forEach((doc) => {
            renderQuestion(doc, review);
          });
        });
      n--;
    }
  }
};

1 Answer 1

1

It looks like you're trying to block the runningExam function until the user presses a button. I found another thread to achieve this.

Basically, turn the renderQuestion function into an asynchronous one, and wait for user input everytime it's called.

renderQuestion = async (qID, roomID) => {
  const timeout = async ms => new Promise(res => setTimeout(res, ms));
  let userClicked = false;

  q.innerHTML = qID.data().question;
  ans1.innerHTML = qID.data().a;
  ans2.innerHTML = qID.data().b;
  ans3.innerHTML = qID.data().c;
  ans4.innerHTML = qID.data().d;
  next.addEventListener("click", (e) => {
    userClicked = true;
    e.preventDefault();
    console.log("done");
  });
  
  while (userClicked === false) await timeout(50);
};

and in runningExam:

then( async (snapshot) => {
      for(let doc of snapshot.docs) {
        await renderQuestion(doc, review);
      };
  });

There's probably better ways to program this task, this is just a possible method to do it the way you were trying to.

Sign up to request clarification or add additional context in comments.

4 Comments

Unfortunately, this isn't working. It keeps showing all the items and doesn't wait for the "next" button just like before :(
My bad, that was because forEach doesn't work well with async/await. I updated the runningExam function to fix that.
Thanks, it worked. But, I am curious to know doesn't await work with forEach?
Here's an explanation with source to the actual implementation of forEach. I don't fully understand it yet either, just learned this today :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.