Обещание выполняет затем функцию до предыдущего, затем выполнение завершено - PullRequest
0 голосов
/ 18 сентября 2018

Я пытаюсь связать пару функций, которые выполняются последовательно, но последний .then () выполняется до того, как завершится предыдущий, и в результате он отправляет пустую полезную нагрузку.Ниже приведен фрагмент:

router.get("/selectedHotels", function(req, res) {
  let payload = [];
  return collectionRef
    .where("isOwner", "==", true)
    .get() //fetches owners
    .then(snapshot => {
      snapshot.forEach(user => {
        console.log("User", user);
        collectionRef
          .doc(user.id)
          .collection("venues")
          .get() // fetches hotels from owners
          .then(snapshot => {
            snapshot.forEach(doc => {
              if (
                doc.data().location.long == req.query.long &&
                doc.data().location.lat == req.query.lat
              ) {
                console.log(doc.id, "=>", doc.data());
                payload.push({
                  id: doc.id,
                  data: doc.data()
                });
              }
            });
          })
          .catch(err => {
            console.log("No hotels of this user", err);
          });
      });
    })
    .then(() => {
      console.log("Payload", payload);
      response(res, 200, "Okay", payload, "Selected hotels");
    })
    .catch(err => {
      console.log("Error getting documents", err);
      response(res, 404, "Data not found", null, "No data available");
    });
});

Есть предложения?Спасибо

Ответы [ 4 ]

0 голосов
/ 24 сентября 2018

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

router.get('/selectedVenues',function(req,res){
    return collectionRef.where('isOwner', '==', true).get() 
    .then(snapshot => {
        let venues = [];
        snapshot.docs.map(user => {
            venues.push(collectionRef.doc(user.id).collection('venues').get());
        });
        return Promise.all(venues);
    }).then(snapshots => {
        let payload = [];
        snapshots.forEach(venues => {
            venues.docs
                .filter(doc => 
                    doc.data().longitude == req.query.lng && 
                    doc.data().latitude == req.query.lat
                )
                .map(doc => 
                    payload.push({
                        id: doc.id,
                        data: doc.data()
                    })
                ) 
        });
        return payload ;
    }).then(payload => {
        console.log('Payload', payload);
        response(res, 200, "Okay", payload, "Selected hotels");
    }).catch(err => {
        console.log('Error getting documents', err);        
        response(res, 404, 'Data not found', null, 'No data available');
    });
});
0 голосов
/ 18 сентября 2018

Ваша главная ошибка в том, что у вас есть функция возврата без обещаний, forEach, в середине вашей вложенной цепочки обещаний.

router.get('/selectedHotels',function(req,res){ 
  let payload = [];
  return collectionRef.where(...).get()
    .then((snapshot)=>{
        snapshot.forEach(user => {
//      ^^^^^^^^^^^^^^^^^ this means the outer promise doesn't wait for this iteration to finish
// ...

Самое простое решение - сопоставить массив обещаний,передайте их в Promise.all и верните их:

router.get('/selectedHotels',function(req,res){ 
  let payload = [];
  return collectionRef.where(...).get()
    .then((snapshot)=> {
      return Promise.all(snapshot.map(
        // ...
        return collectionRef.doc(user.id).collection('venues').get()
          .then(...)
      ))

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

Вместо этого вы должны располагать их вертикально.

Вот пример того, как вы можете это сделать:

router.get("/selectedHotels", function(req, res) {
  return collectionRef
    .where("isOwner", "==", true)
    .get() //fetches owners
    // portion of the chain that fetches hotels from owners
    // and propagates it further
    .then(snapshot =>
      Promise.all(
        snapshot.map(user =>
          collectionRef
            .doc(user.id)
            .collection("venues")
            .get()
        )
      )
    )
    // this portion of the chain has the hotels
    // it filters them by the req query params
    // then propagates the payload array
    // (no need for global array)
    .then(snapshot =>
      snapshot
        .filter(
          doc =>
            doc.data().location.long == req.query.long &&
            doc.data().location.lat == req.query.lat
        )
        .map(doc => ({ id: doc.id, data: doc.data() }))
    )
    // this part of the chain has the same payload as you intended
    .then(payload => {
      console.log("Payload", payload);
      response(res, 200, "Okay", payload, "Selected hotels");
    })
    .catch(err => {
      console.log("Error getting documents", err);
      response(res, 404, "Data not found", null, "No data available");
    });
});
0 голосов
/ 18 сентября 2018

Вы не возвращаете обещание из своего первого then, поэтому код не может знать, что ему следует ожидать асинхронного результата.

router.get('/selectedHotels',function(req,res){ 
    let payload = [];
    return collectionRef.where('isOwner', '==', true).get() //fetches owners
    .then((snapshot)=>{
        var userVenuesPromises = [];
        snapshot.forEach(user => {
            userVenuesPromises.push(collectionRef.doc(user.id).collection('venues').get());

        })
        return Promise.all(userVenuesPromises);
    })
    .then((snapshots) => {
        snapshots.forEach((snapshot) => {
            snapshot.forEach((doc)=> {
                if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
                    console.log(doc.id, '=>', doc.data());
                    payload.push({
                        id: doc.id,
                        data: doc.data()
                    });
                }
            });
        });
        return payload;
    })
    .then((payload) => {
        ...

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

0 голосов
/ 18 сентября 2018

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

    return Promise.all(snapshot.map(user => {
            console.log("User", user);
            return collectionRef.doc(user.id).collection('venues').get() // fetches hotels from owners
            .then(snapshot => {
                snapshot.forEach((doc)=> {
                        if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
                            console.log(doc.id, '=>', doc.data());
                            payload.push({
                                id: doc.id,
                                data: doc.data()
                            });
                        }
                    });
                }).catch((err)=>{
                    console.log('No hotels of this user', err);        
                });        
        });
    )

Вы можете увидеть это в действии в следующем примере:

function asyncStuff() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('async')
      resolve();
    }, 100)
  });
}

function doStuff() {
  console.log('started');
  asyncStuff()
  .then(() => {
    return Promise.all([0,1,2].map(() => asyncStuff()));
  })
  .then(() => {
    console.log('second then');
  })
  .then(() => console.log('finished'));
}

doStuff();

И посмотрите, что без возврата это дает ваше начальное поведение:

function asyncStuff() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('async')
      resolve();
    }, 100)
  });
}

function doStuff() {
  console.log('started');
  asyncStuff()
  .then(() => {
    Promise.all([0,1,2].map(() => asyncStuff()));
  })
  .then(() => {
    console.log('second then');
  })
  .then(() => console.log('finished'));
}

doStuff();
...