Внутреннее обещание Firestore внутри forEach, выполняется после внешнего обещания - PullRequest
0 голосов
/ 26 декабря 2018

Я использую node.js для получения данных из 2 разных коллекций в Firestore.

Эта проблема похожа на эту, но на этот вопрос нет ответа: Nested Firebase Firestore forEach обещаниезапросы

В моем случае я создаю приложение в стиле Instagram, в котором у меня есть коллекция временных шкал.Внутри документа временной шкалы у меня есть ключ пользователя.Из этого пользовательского ключа я бы хотел выполнить другой запрос из коллекции 'user'.

Итак, логически здесь приведены шаги запроса:

  1. Получить данные временной шкалы (в массиве), которыесодержит ключ пользователя.
  2. Используя этот ключ пользователя, получить пользовательские данные (отдельные данные)
  3. Возвращать JSON-ответ клиенту.

Проблема в том, что JSON-ответвозвращается до получения пользовательских данных.

Это мой код:

tlRoute.get((req,res)=>{

  //reading from firestore
  let afs = req.app.get('afs');

  var timelineArr = [];

  let timelineRef = afs.collection(`timeline/${req.params.key}/timeline`);
  timelineRef.get().then((snapshot) => {

    snapshot.forEach((doc) => {

      if(!doc.exists){
        console.log('No such document!');
      } else {

        //populating timelineData with doc.data()
        console.log('populating timelineData');
        let timelineData = doc.data();

        let userRef = afs.doc(`user/${doc.data().userKey}`);
        userRef.get().then((doc) => {

          //adding user details to timelineData
          console.log('adding user details to timelineData');
          timelineData.profileImageUrl = doc.data().profileImageUrl;
          timelineData.username = doc.data().username;
          timelineArr.push(timelineData);

        });
      }

    });
  })
  .catch(err => {
    console.log(err);
  });

  console.log('this is the final timelineArr', timelineArr);

  //returning response json data to client
  return res.json(timelineArr);

});

В журнале консоли это журнал, который я получаю:

this is the final timelineArr []
populating timelineData
populating timelineData
adding user details to timelineData
adding user details to timelineData

Любая помощьбыл бы признателен.

Ответы [ 3 ]

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

Я решил использовать for для обхода внутреннего обещания.

Вот мой код:

tlRoute.get((req,res)=>{

  let db = req.app.get('db');
  let key = req.params.key;

  var timelineArr = [];

  getData(db, key, timelineArr).then(results => {

    for (let index = 0; index<results.length; index++) {
      timelineArr[index].profileImageUrl = results[index].data().profileImageUrl;
      timelineArr[index].username = results[index].data().username;
    }
    console.log(results.length);

    console.log(timelineArr);

    console.log('this is the final timelineArr');
    //returning response json data to client 
    return res.json(timelineArr);

  });

});

function getData(db, key, timelineArr){
  let timelineRef = db.collection(`timeline/${key}/timeline`);
  return timelineRef.get().then((snapshot) => {

    console.log('snapshot length: ', snapshot.length);

    var promiseArr = [];

    snapshot.forEach((doc) => {

      if(!doc.exists){
        console.log('No such document!');
      } else {

        //populating timelineData with doc.data()
        console.log('populating timelineData');
        let timelineData = doc.data();
        timelineData.postKey = doc.id;
        timelineArr.push(timelineData);

        console.log('userKey: ', doc.data().userKey);
        let userRef = db.doc(`user/${doc.data().userKey}`);
        promiseArr.push(userRef.get());

      }
    });
    return Promise.all(promiseArr);

  })
  .catch(err => {
    console.log(err);
  });
}
0 голосов
/ 03 января 2019

Я пытался реорганизовать ваш код для получения того же результата, добавив несколько методов, каждый из которых имеет более простую, изолированную, тестируемую цель, относящуюся к конкретному виду объекта.

// return a promise that resolves to a user doc snapshot with the given key
function getUserWithKey(db, userKey) {
  return db.doc(`user/${userKey}`).get();
}

// return a promise that resolves to a timeline object augmented to include
// its doc id and its associated user's username
function timelineObject(db, timelineDocSnapshot) {
  let timelineObject = timelineDocSnapshot.data();
  timelineObject.postKey = timelineDocSnapshot.id;
  return getUserWithKey(db, timelineObject.userKey).then(userDocSnapshot => {
    timelineObject.username = userDocSnapshot.data().username;
    timelineObject.profileImageUrl = userDocSnapshot.data().profileImageUrl;
    return timelineObject;
  });
}

// return a promise that resolves to all of the timeline objects for a given key
function getTimeline(db, key) {
  let timelineRef = db.collection(`timeline/${key}/timeline`);
  return timelineRef.get().then(querySnapshot => {
    let promises = querySnapshot.docs.map(doc => timelineObject(db, doc));
    return Promise.all(promises);
  });
}

// get route for a timeline
tlRoute.get((req,res)=>{
  let db = req.app.get('db');
  let key = req.params.key;
  return getTimeline(db, key).then(timelineObjects => {
    return res.json(timelineObjects);
  });
})

Этокод может быть улучшен с использованием синтаксиса async / await.

0 голосов
/ 26 декабря 2018

Sooo Firebase использует обратные вызовы или обещания (штука «.then ((snapshot) => {})», которая запускается после получения данных из Firestore.Что вы делаете, так это возвращаете timelineArr до запуска callback-метода и, следовательно, до того, как он будет заполнен данными из Firestore!

Одним из решений этого является перемещение оператора return внутри callback-метода и выполнение всей функции асинхронной.Это будет примерно так:

var timelineArr = [];
async function RetrieveData() {
    let timelineRef = afs.collection(`timeline/${req.params.key}/timeline`);
    await timelineRef.get().then((snapshot) => {

    snapshot.forEach((doc) => {

      if(!doc.exists){
        console.log('No such document!');
      } else {

        //populating timelineData with doc.data()
        console.log('populating timelineData');
        let timelineData = doc.data();

        let userRef = afs.doc(`user/${doc.data().userKey}`);
        userRef.get().then((doc) => {

          //adding user details to timelineData
          console.log('adding user details to timelineData');
          timelineData.profileImageUrl = doc.data().profileImageUrl;
          timelineData.username = doc.data().username;
          timelineArr.push(timelineData);

        });
      }

    });

      //returning response json data to client
      return res.json(timelineArr);

    }).catch(err => {
      console.log(err);
    });
}

RetrieveData()

console.log('this is the final timelineArr', timelineArr);

Удачи!

С уважением, Эскильс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...