Возврат облачных данных Firebase из нескольких обещаний одновременно - PullRequest
0 голосов
/ 06 марта 2019

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

Вот код:

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })
      
      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {
    const results = [];
    citySnapshots.forEach(citySnap => {
      const data = citySnap.data();
      // this log entry is occuring once for each p and p2 from above.
      // I tried an array reference like citySnap[0] for mentee info and citySnap[1] for mentor info, this did not work.
      console.log("cSnap is: ", data);
      results.push(data);

    })
    return Promise.all(results);
  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

В результате получается, что я получаю имя и фамилию ученика, а затем вывод для имени и фамилии наставника (в отдельной строке).

В Firestore каждый документ в коллекции совпадений - это просто идентификатор наставника и массив идентификаторов ученика.Вся информация о пользователях хранится в коллекции пользователей.Итак, я пытаюсь пройтись по каждому документу на совпадения и создать один ряд данных для каждой комбинации наставник / ученик.

Мне все еще нужно добавить некоторую обработку, когда p и / или p2 недоступны.

Моим первоначальным намерением для 'p' и 'p2' было:

  1. вернуть имя и фамилию для p, переименовать их в menteeFirstName и menteeLastName

  2. вернуть имя и фамилию для p2, переименовать их mentorFirstname и mentorLastName

  3. объединить эту информацию и вернуть массив mentorFirstName, mentorLastName, menteeFirstName, menteeLastName.

Однако я спустился в кроличью нору с этим.Я решил сократить его до рабочего кода и опубликовать это.

Итак, можно ли объединить данные из 'p' и 'p2'?Или я делаю это неправильно?

Я родом из реляционной базы данных, поэтому концепция коллекций / документов Firestore с асинхронными вызовами является для меня новой концепцией, с которой я становлюсь более знакомой (но не достаточнопока).

Я пытался понять различные примеры, но я думаю, что мое незнакомство с обещаниями сейчас является серьезным препятствием.Я испробовал и Roamer, и предложения Стивена Сарка.Предложение Роамера не ошибается, но я считаю, что оно отбрасывает обещания.

exports.getMatchesNew = functions.https.onCall((data, context) => {
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
          var area = doc.data();
          for (let city in area.mentees) {
              const p = db.collection('users').doc(area.mentees[city]).get();
              const p2 = db.collection('users').doc(doc.id).get();
              promises.push(
                  Promise.all([p, p2]) // aggregate p and p2
                  .then(([mentee, mentor]) => {
                    var mentorInfo = mentor.data();
                    var menteeInfo = mentee.data();

                      console.log("Mentor: ", mentorInfo.lastName);
                      console.log("mentee: ", menteeInfo.lastName);
                      // return a plain object with all the required data for this iteration's doc
                      return {
                          // 'area': area, // for good measure
                          // 'city': city, // for good measure
                          'mentee': menteeInfo, // ???
                          'mentor': mentorInfo // ???
                      };
                  })
              );
          }
      })
      return Promise.all(promises);
  })
  .catch(error => {
      console.log(error);
      //response.status(500).send(error);
  });
});

Я вижу данные в журналах, но записи не возвращаются (или я неправильно ссылаюсь на них на странице .vue

<template slot="mentorLastName" slot-scope="row">
          {{row.item.mentor.lastName}}
        </template>
        <template slot="menteelastName" slot-scope="row">
          {{row.item.mentee.lastName}}
        </template>

Это работает в других случаях, когда результаты содержат эти же объекты.

Код Стивена Сарка также выполняется на основе файлов журнала без ошибок. Разница в том, что страница vue никогда не возвращается (всегда отображается как «мышление»'). В прошлом это означало, что в облачной функции произошла ошибка. В журналах облачных функций нет ошибок, и данные НЕ отображаются в журналах консольной функции (тогда как в версии Roamer).Я не могу доказать, что работает.

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    console.log("Mentor: ", mentor.lastName);
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

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

Ответы [ 3 ]

0 голосов
/ 07 марта 2019

Ваш подход может быть реализован, но он довольно грязный из-за формирования одного массива promises, содержащего как p s, так и p2 s.

Насколько я могу судить, вы хотите объединить необходимые данные для каждого документа в объект.

Есть несколько способов сделать это. Вот пример использования внутреннего Promise.all() для агрегирования каждой пары [p,p2] обещаний, а затем формирования требуемого объекта.

exports.getMatchesNew = functions.https.onCall((data, context) => {
    return db.collection('matches').get()
    .then(areaSnapshot => {
        const promises = [];
        areaSnapshot.forEach(doc => {
            var area = doc.data();
            for (let city in area.mentees) {
                const p = db.collection('users').doc(area.mentees[city]).get();
                const p2 = db.collection('users').doc(doc.id).get();
                promises.push(
                    Promise.all([p, p2]) // aggregate p and p2
                    .then(([mentee, mentor]) => {
                        // return a plain object with all the required data for this iteration's doc
                        return {
                            'area': area, // for good measure
                            'city': city, // for good measure
                            'mentee': mentee.data(), // ???
                            'mentor': mentor.data() // ???
                        };
                    });
                );
            }
        })
        return Promise.all(promises);
    })
    .catch(error => {
        console.log(error);
        //response.status(500).send(error);
    });
});

Все при условии, что .get() является асинхронным и .data() является синхронным

Путь успеха возвращенного обещания доставит массив {area,mentee,mentor} объектов.

Может быть не на 100% правильно, потому что я не до конца понимаю данные. Не должно быть слишком сложно адаптироваться.

0 голосов
/ 07 марта 2019

Это окончательный рабочий код.Благодаря @ Roamer-1888 и "@Steven Stark"

Финальный код - это нечто среднее между обоими их предлагаемыми решениями.Очень признателен за помощь.

В результате мне стало немного легче с обещаниями.Надеюсь, мне удастся поработать с ними немного больше, чтобы почувствовать себя более комфортно и улучшить свои шансы на его сохранение.

exports.getMatchesNew = functions.https.onCall((data, context) => {
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
          var area = doc.data();
          for (let city in area.mentees) {
              const p = db.collection('users').doc(area.mentees[city]).get();
              const p2 = db.collection('users').doc(doc.id).get();
              if ( typeof p !== 'undefined' && p && typeof p2 !== 'undefined' && p2)
              {
                promises.push(
                    Promise.all([p, p2]) // aggregate p and p2
                );
              }
          }
      })
      return Promise.all(promises);
  })
    .then(citySnapshots => {
    const results = [];
    citySnapshots.forEach(citySnap => {
      var mentor = citySnap[1].data();
      var mentee = citySnap[0].data();
      
      if ( typeof mentee !== 'undefined' && mentee && typeof mentor !== 'undefined' && mentor)
      {
        var matchInfo = {
          mentor: {},
          mentee: {}
        }
        matchInfo.mentor = mentor;
        matchInfo.mentee = mentee;

        results.push(matchInfo);
      }
      else
      {
        console.log("Error missing mentor or mentee record, Need to research: ");
      }
    })
    return Promise.all(results);
  })
  .catch(error => {
      console.log(error);
      //response.status(500).send(error);
  });
});
0 голосов
/ 06 марта 2019

Перемещен к ответу, потому что он слишком велик для комментария:

Я никогда не сталкивался с примерами, использующими Promise.all таким образом (с использованием массива данных), но, возможно, это пример из более ранней библиотеки Promise, такой как Bliebird, которая больше не применима.

вот вам быстрый пример:

Promis.all([ Promise.resolve('one'), Promise.reject(new Error('example failure') ])
.then( ( values ) => {
  //values[0] = data from promise 1; 'one'
  //values[1] = data from promise 2
})
.catch( ( error ) => {
  // note that an error from any promise provided will resolve this catch
})

EDIT:

В соответствии с запросом, ваш код изменен для использования Promise.all:

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

еще один пример без вашей логики БД, которому я не совсем следую:


var response1 = { "foo": "bar" }
var response2 = { "biz": "baz" }


  return Promise.resolve( [ response1, response2 ] )
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
          promises.push(Promise.resolve( doc ));
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });


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