【问题标题】:Pending promises even though awaiting Promise.all()即使在等待 Promise.all() 也有待处理的承诺
【发布时间】:2021-11-28 00:17:55
【问题描述】:

我很难理解为什么在等待Promise.all() 之后我仍然有未决的承诺。

在下面的示例中,我使用 .map() 对数组的每个元素调用异步函数来创建一个 Promise 数组。

现在,为什么承诺仍然显示为待处理?我现在(错误)理解它的方式:

  • 一旦来自storeData() 的承诺解决,then() 就会触发
  • storeData()resolves 一旦 newDataArray 返回
  • 一旦promisesArray 中的所有承诺都已解决或第一个承诺被拒绝,newDataArray 就会返回。
storeData(OldDataArray).then(values => console.log(values))
// console shows:
// { id: 1, data: Promise { <pending> } },
// { id: 2, data: Promise { <pending> } }

const storeData = async (OldDataArray) => {
  try {
      const promisesArray = OldDataArray.map((item) => {
      let newData = downloadMoreDetails(item.id, item.group); //async function, see below
      return {
        id: item.id,
        data: newData,
      };
    });
    const newDataArray = await Promise.all(promisesArray);  // <-- I'm awaiting all promises to complete before assigning to newDataArray
    return newDataArray;
  } catch (error) {
    console.log(error)
  }
};
const downloadMoreDetails = async (id, group) => {
  const response = await fetch(
    `example.com/id/group.xml`
  );
  if (!response.ok) {
    throw new Error(`HTTP error ${response.status}`);
  }

  const str = await response.text();
  const json = convert.xml2json(str, {
    compact: true,
    spaces: 2,
  });
  return json;
};

【问题讨论】:

  • promisesArray 不是一组承诺,这就是为什么等待它什么都不做。
  • 谢谢!如果异步函数总是返回一个promise,并且我在map 中调用了一个异步函数,为什么map 的结果不是一个promise 数组?
  • If async functions always return a promise and I'm calling an async function inside map 因为作为异步函数和调用异步函数是不同的东西。您传递给 map 的函数不是异步的。
  • newData 是一个承诺,但您正在等待{id: item.id, data: newData } 的数组。 Promise.all() 不会在这些对象内部查找承诺并等待该承诺。它只看到一组普通对象,这意味着它没有任何事情要做。
  • 向你的数组添加承诺,而不是对象...OldDataArray.map(({ id, group }) =&gt; downloadMoreDetails(id, group).then(data =&gt; ({ id, data })))

标签: javascript node.js promise


【解决方案1】:

newData 是一个承诺,但您正在等待{id: item.id, data: newData } 的数组。 Promise.all() 不会在这些对象内部查找承诺并等待该承诺。它只看到一个普通对象数组,这意味着它无事可做。你可以通过这样做来解决这个问题:

const storeData = async (OldDataArray) => {
  try {
    const promisesArray = OldDataArray.map(async (item) => {
        let newData = await downloadMoreDetails(item.id, item.group); //async function, see below
        return {
          id: item.id,
          data: newData,
        };
    });
    return Promise.all(promisesArray);
  } catch (error) {
    // log and rethrow error so the caller gets the rejection
    console.log(error);
    throw error;
  }
};

这会将.map() 回调更改为async。这有两件有益的事情。首先,这意味着来自.map() 的结果数组将是一个promise 数组,因为async 回调总是返回一个promise。其次,它允许您在回调中使用await,这样您就可以使用实际数据填充返回的对象,而不是使用承诺。


注意,您也可以在不添加 async/await 的情况下这样做:

const storeData = (OldDataArray) => {
    const promisesArray = OldDataArray.map((item) => {
       return downloadMoreDetails(item.id, item.group).then(newData => {
          return {
            id: item.id,
            data: newData,
          };
       });
    });
    return Promise.all(promisesArray).catch(error => {
      // log and rethrow error so the caller gets the rejection
      console.log(error);
      throw error;
    });
};

在此版本中,您直接从 .map() 回调返回一个承诺,并确保该承诺解析为您的数据对象。

【讨论】:

  • 这是有道理的。所以用简单的英语来说,通过使用异步函数映射数组,结果数组中的每个项目都将是一个承诺(这很好,因为这样我们可以用Promise.all 等待该数组)。然后,数组中的每个 promise 都会根据 downloadMoreDetails 返回的内容来解析/拒绝。我想我有它..
  • @SPi - 请参阅我添加的不使用 async/await 的附加解决方案,这样您也可以了解如何做到这一点。
猜你喜欢
  • 1970-01-01
  • 2020-12-18
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 2013-09-16
  • 1970-01-01
  • 2019-09-29
  • 2022-01-22
相关资源
最近更新 更多