即使等待 Promise.all() 也未决承诺
Pending promises even though awaiting Promise.all()
我很难理解为什么在等待 Promise.all() 之后我仍然有未决的承诺。
在下面的示例中,我通过使用 .map()
.
在数组的每个元素上调用异步函数来创建一个承诺数组
现在,为什么承诺仍显示为待处理?我现在(错误)理解它的方式:
then()
在 storeData()
的承诺解决后触发
storeData()
返回一次newDataArray
解决
newDataArray
在 promisesArray
中的所有承诺都得到解决或第一个拒绝后返回。
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;
};
newData
是一个承诺,但你不是在等待承诺。相反,您正在等待一个包含 promise 的对象数组 {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()
的结果数组将是一个承诺数组,因为 async
回调总是 return 一个承诺。其次,它允许您在回调中使用 await
,因此您可以使用实际数据而不是承诺来填充 returned 对象。
然后,来自 async
回调内部的 return
将导致该值成为 async
函数正在 returning 的承诺的已解析值。
请注意,您也可以在不添加 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;
});
};
在此版本中,您直接 return 来自 .map()
回调的承诺,并确保该承诺解析为您的数据对象。
我很难理解为什么在等待 Promise.all() 之后我仍然有未决的承诺。
在下面的示例中,我通过使用 .map()
.
现在,为什么承诺仍显示为待处理?我现在(错误)理解它的方式:
then()
在storeData()
的承诺解决后触发storeData()
返回一次newDataArray
解决newDataArray
在promisesArray
中的所有承诺都得到解决或第一个拒绝后返回。
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;
};
newData
是一个承诺,但你不是在等待承诺。相反,您正在等待一个包含 promise 的对象数组 {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()
的结果数组将是一个承诺数组,因为 async
回调总是 return 一个承诺。其次,它允许您在回调中使用 await
,因此您可以使用实际数据而不是承诺来填充 returned 对象。
然后,来自 async
回调内部的 return
将导致该值成为 async
函数正在 returning 的承诺的已解析值。
请注意,您也可以在不添加 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;
});
};
在此版本中,您直接 return 来自 .map()
回调的承诺,并确保该承诺解析为您的数据对象。