首页 > 解决方案 > 在返回节点之前等待嵌套循环完成

问题描述

我正在尝试将三次嵌套循环中的结果推送到结果数组中,最后返回该数组。我认为拥有 .then 会实现我正在寻找的行为,但该函数会立即返回一个空数组。我尝试过使用 async/await、.map 和 for 循环的变体,但没有成功。

exports.getMasterTenantList = functions.https.onRequest((req, res) => {
  let result = [];
  cors(req, res, () => {
    admin
      .database()
      .ref("/property_names")
      .once("value")
      .then((snapshot) => {
        snapshot.forEach((childSnapshot) => {
          const propertyName = childSnapshot.key;
          admin
            .database()
            .ref(`/property_groups/${propertyName}/locations`)
            .once("value")
            .then((addresses) => {
              return addresses.forEach((addressSnapshot) => {
                const address = addressSnapshot.key;
                admin
                  .database()
                  .ref(`/property_groups/${propertyName}/locations/${address}/residents`)
                  .once("value")
                  .then((residents) => {
                    return residents.forEach((resSnapshot) => {
                      result.push(resSnapshot.val().name);
                    });
                  })
                  .catch((error) => {
                    console.log(error);
                  });
              });
            })
            .catch((error) => {
              console.log(error);
            });
        });
        return res.status(200).send(result);
      })
      .catch((error) => {
        console.log(error);
      });
  });
});

标签: javascriptnode.jspromise

解决方案


您的错误是它return addresses.forEach不会返回您认为的结果。

您应该遵循您的代码并等待每个请求返回。你可以使用Promise.all它。


我会去做这样的事情:

exports.getMasterTenantList = functions.https.onRequest(async(req, res) => {
  await cors(req, res);

  const snapshot = await admin
    .database()
    .ref('/property_names')
    .once('value');

  const result = await Promise.all(snapshot.forEach((childSnapshot) => {
    const propertyName = childSnapshot.key;

    const addresses = await admin
      .database()
      .ref(`/property_groups/${propertyName}/locations`)
      .once('value');

    return Promise.all(addresses.map(async(addressSnapshot) => {
      const address = addressSnapshot.key;

      const residents = await admin
        .database()
        .ref(`/property_groups/${propertyName}/locations/${address}/residents`)
        .once('value');

      return residents.map(resSnapshot => resSnapshot.val().name);
    }));
  }));

  return res.status(200).send(result);
});

要了解它是如何工作的,请查看以下代码段示例:

const cors = () => new Promise((resolve) => setTimeout(resolve, 100));

const getSnapshot = () => new Promise((resolve) => setTimeout(() => {
  resolve([{
    key: 'dog',
  }]);
}, 100));

const getAddress = () => new Promise((resolve) => setTimeout(() =>  {
  resolve([{
    key: '11 richmond street',
  }]);
}, 100));

const getResident = () => new Promise((resolve) => setTimeout(() => {
  resolve([{
    val: () => ({
      name: 'John doe',
    }),
  }]);
}, 100));

async function onRequest(res) {
  await cors();

  const snapshot = await getSnapshot();

  const result = await Promise.all(snapshot.map(async (childSnapshot) => {
    const propertyName = childSnapshot.key;

    const addresses = await getAddress();

    return Promise.all(addresses.map(async(addressSnapshot) => {
      const address = addressSnapshot.key;

      const residents = await getResident();

      return residents.map(resSnapshot => resSnapshot.val().name);
    }));
  }));

  return res.status(200).send(result);
}

onRequest({
  status: () => ({
    send: console.log,
  }),
});


我什至会考虑将代码分解为几个函数,使其更易于阅读和理解。

function getSnapshot() {
  return admin
    .database()
    .ref('/property_names')
    .once('value');
}

function getAddressesByPropertyName(propertyName) {
  return admin
      .database()
      .ref(`/property_groups/${propertyName}/locations`)
      .once('value');
}

async function getResidentNameByAddress({
  propertyName,
  address,
}) {
  return (await admin
        .database()
        .ref(`/property_groups/${propertyName}/locations/${address}/residents`)
        .once('value')).map(resSnapshot => resSnapshot.val().name);
}

exports.getMasterTenantList = functions.https.onRequest(async(req, res) => {
  await cors(req, res);

  const snapshot = await getSnapshot();

  const result = await Promise.all(snapshot.forEach(({
    key: propertyName,
  }) => {
    const addresses = await getAddressesByPropertyName(propertyName);

    return Promise.all(addresses.map(async({
      key: address,
    }) => {
      return getResidentNameByAddress({
        propertyName,
        address,
      });
    }));
  }));

  return res.status(200).send(result);
});


推荐阅读