Promise 在上一个 then 执行完成之前执行 then 函数

Muh*_*las 2 javascript node.js promise firebase es6-promise

我尝试链接几个按顺序执行的 then 函数,但最后一个 .then() 是在前一个 .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");
    });
});
Run Code Online (Sandbox Code Playgroud)

有什么建议么?谢谢

nem*_*035 5

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
// ...
Run Code Online (Sandbox Code Playgroud)

最简单的解决方法是映射您的 Promise 数组,将它们传递给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(...)
      ))
Run Code Online (Sandbox Code Playgroud)

话虽这么说,像这样的嵌套承诺是一种反模式。Promise 链允许我们通过 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");
    });
});
Run Code Online (Sandbox Code Playgroud)