Объект коллекции возвращен как неопределенный, используя async / await - PullRequest
0 голосов
/ 18 мая 2019

Я настраиваю экспресс-приложение для обработки SSR для моего приложения реагирования. Мне нужно получить доступ к содержимому Firebase по одному из моих маршрутов, который определен как логическое значение в моем файле rout.js.

export default [
  {
    component: Home,
    path: '/',
    exact: true,
    fetchInitialData: true
  },
  {
    component: About,
    path: '/about',
    fetchInitialData: false
  }
];

Это импортируется через мое экспресс-приложение как обычное серверное приложение, чтобы определить, имеет ли для этого логического значения для маршрута значение true , после чего я могу передать результат функции в качестве опоры моему приложению. компоненты.

app.get('*', async (req, res) => {
//Get the matching route object for the URL
  const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}

  let interaction = {}
  const context = {};
  const modules = [];

//If the route object has a fetchInitialData boolean as truthy, set interaction to the result of getDownloadURL
  if(activeRoute.fetchInitialData){
    interaction = await getDownloadURL(req.query.id)
  }

  //THIS IS UNDEFINED, despite using async/await.
  console.log(interaction)

  //Interaction should be set from here. Process rest of app logic

Я столкнулся с препятствием на пути к API Firebase, потому что я не могу включить SDK Firebase-Admin в свое приложение реакции. Поэтому я должен запустить его в экспресс-среде и передать данные в качестве подпорки, поэтому я использую логическое значение для требуемых данных вместо передачи функции непосредственно в приложение. Я не могу использовать обычный firebase-sdk, потому что мне нужно использовать функцию хранилища getDownloadURL, которая использует XMLHttpRequest, который не совместим с сервером, только с браузером!

Я определил свою собственную функцию getDownloadURL в файле экспресс-приложения, которая принимает параметр запроса в качестве идентификатора документа и обрабатывает downloadURL для изображения, хранящегося в этом документе.

const getDownloadURL = (path) => {
  if(path){
    firestore.collection('interactions').doc(path).get()
      .then(doc => {
        bucket
        .file(doc.data().photoPath)
        .getSignedUrl({
          action: 'read',
          expires: '03-09-2999'
        })
        .then(photoURL => {  
          const interaction = {
            date: doc.data().date,
            venue: doc.data().venue,
            photoURL: photoURL
          }
          //console.log(interaction) <<<<< this returns the valid data.
          return interaction;     
        }).catch(e => console.log(e));
      }).catch(e => console.log(e));
  }else{
    return {};
  }
}

Это обработает документ firebase и в конечном итоге получит photoURL из API хранилища, который я возвращаю вызывающему объекту в качестве объекта.

Я пытался использовать Promises вместо async / await, следуя этой структуре из этого руководства: https://tylermcginnis.com/react-router-server-rendering

  const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}

  const promise = activeRoute.fetchInitialData
    ? getDownloadURL(req.query.id)
    : Promise.resolve()

  promise.then((data) => {
  //Rest of app logic, data is still undefined

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

const getDownloadURL = (path) => {
  if(path){
    firestore.collection('interactions').doc(path).get()
      .then(doc => {
        return bucket
          .file(doc.data().photoPath)
          .getSignedUrl({
            action: 'read',
            expires: '03-09-2999'
          })
      }).catch(e => console.log(e));
  }else{
    return {};
  }
}

Тем не менее, результат этой функции всегда неопределен, и я не уверен, как это исправить в этой точке. Любая помощь приветствуется. Спасибо.

Ответы [ 2 ]

0 голосов
/ 18 мая 2019

Можно ли как-нибудь оптимизировать это?

Смешивание async-await с then-catch не очень хорошо для читабельности.Если вы можете помочь, вы можете продолжить рефакторинг, чтобы ваш код использовал только async-await.

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

1) Код для получения документа пожарного депо не слишком многословен, и вам, кажется, нужен документ вне функции getDownloadURL, так что вывозможно, стоит просто переместить это в вашу основную функцию:

  if(activeRoute.fetchInitialData && req.query.id){
    const snap = await firestore.collection('interactions').doc(req.query.id).get()
    const data = snap.data()
    // ... 

2) Вы можете просто сделать getDownloadURL асинхронной функцией, и вам не придется беспокоиться о возврате обещаний.Асинхронные функции неявно возвращают обещания.Кроме того, вместо указания пути к документу getDownloadURL может получить photoPath, извлечь photoURL и затем вернуть его.

const getDownloadURL = async (photoPath) => {
  if(!photoPath){
    return {}
  }
  const photoURL = await bucket.file(photoPath)
    .getSignedUrl({
      action: 'read',
      expires: '03-09-2999'
    })
  return photoURL // assuming here that photoURL is an object
}

3) Наконец, вы можете заменить остальную часть кода некоторым асинхронным-Ждите.Я также упростил некоторые из логики здесь.

app.get('*', async (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}

  let interaction = {}  

  //If the route object has a fetchInitialData boolean as truthy
  // set interaction to the result of getDownloadURL
  if(activeRoute.fetchInitialData && req.query.id){
    try {
      const snap = await firestore.collection('interactions').doc(req.query.id).get()
      const data = snap.data()
      const photoURL = await getDownloadURL(data.photoPath)
      interaction = {
        date: data.date,
        venue: data.venue,
        photoURL: photoURL
      }   
    } catch (error) {
      return next(error) // return is important here
    }       
  }
  renderApp(req, res, interaction);
})

Ваш другой вариант здесь, я думаю, это переименовать getDownloadURL в getInteraction и заставить его возвращать полное взаимодействие, а не только URL, но это зависит от вас.

Это кажется довольно неуклюжим решением, поскольку мне приходится передавать req и res другой функции для обработки логики рендеринга.

Лично я не вижу в этом ничего плохого.

0 голосов
/ 18 мая 2019

Спасибо @KhauriMcClain, я провел некоторый рефакторинг кода с учетом того, что вы сказали, и заставил его работать, в итоге я обработал обещание от вызывающей стороны, но я смог получить необходимые данные для приложения, которое идеально. Стоит отметить, что мне пришлось задействовать свою логику рендеринга, потому что взаимодействие определяется только в рамках .then, и я не хотел копировать и вставлять свой код.

app.get('*', async (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}

  let interaction = {}  

  //If the route object has a fetchInitialData boolean as truthy
  // set interaction to the result of getDownloadURL
  if(activeRoute.fetchInitialData && req.query.id){
    interaction = await getDownloadURL(req.query.id)
      .then(doc => {
        bucket
        .file(doc.data().photoPath)
        .getSignedUrl({
          action: 'read',
          expires: '03-09-2999'
        })
        .then(photoURL => { 
          interaction = {
            date: doc.data().date,
            venue: doc.data().venue,
            photoURL: photoURL
          }          
          renderApp(req, res, interaction);
        }).catch(next)
      }).catch(next)
  }else{
    renderApp(req, res, interaction);
  }
})

И вот теперь getDownloadURL

const getDownloadURL = (path) => {
  if(path){
    return firestore.collection('interactions').doc(path).get()
  }else{
    return Promise.resolve({});
  }
}

Есть ли лучший способ справиться с этим, хотя? Это кажется довольно неуклюжим решением, поскольку мне приходится передавать req и res другой функции для обработки моей логики рендеринга.

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