[英]How to return a Promise from async function?
当我尝试从异步函数返回promise时,无法区分返回的Promise和函数的状态。
我认为,最简单的解决方案是将promise返回到数组中。 以下是一个愚蠢的示例,但我希望它能演示该问题:
function loadMetaData(id) {/*...*/} // Returns Promise<MetaData>
function loadSingleData(name) {/*...*/} // Returns Promise<SingleData>
async function startLoadingSingleData(id, object) {
const metaData = object.metaData = await loadMetadata(id);
const singleDataPromise = loadSingleData(metaData.dataToLoad);
singleDataPromise.then(metaData => object.metaData = metaData);
return [singleDataPromise];
}
async function logData(id) {
const object = new SomeUsefulClassWhatTakesAdvantageOfMetadataProp();
somePromise = (await startLoadingSingleData(id))[0];
// Now metadata surely loaded, do something with it
console.log(object.metaData);
// But somedata will be loaded only in future
somePromise.then(singleData => console.log(singleData));
// And maybe: (depends of use-case)
await somePromise;
}
当执行logData(/*...*/)
,首先是在很短的时间后,然后是稍singleData
的完整singleData
数据,然后是给定数据的给定ID的metaData
。
但这有点荒谬。
解决这种情况的预期方法是什么?
PS .:当我尝试返回以诺言解决的Promise时,也会发生此问题。
是的,不幸的是,JS承诺不是代数的, 您不能用另一个诺言来履行承诺 。 没有办法解决(除了不使用本机promise,不使用async
/ await
)。
实际上,最简单,最常见的解决方案是使用包装对象。 这自然是您的问题:
// takes an id, returns a Promise<{metaData: Data, singleDataPromise: Promise<Data>}>
async function startLoadingSingleData(id) {
const object = new SomeUsefulClassWhatTakesAdvantageOfMetadataProp();
object.metaData = await loadMetadata(id);
// ^^^^^
object.singleDataPromise = loadSingleData(object.metaData.dataToLoad);
// ^ no await here
return object;
}
async function logData(id) {
const object = await startLoadingSingleData(id));
// Now metadata surely loaded, do something with it
console.log(object.metaData);
// But some singleData will be loaded only in future
const singleData = await object.singleDataPromise;
console.log(singleData);
}
请注意,如果您的代码中存在异常并且您永远也singleDataPromise
await
singleDataPromise
,这可能导致未处理的拒绝问题。
(可能更好)的替代方法是重组函数,以便在使用(即等待)它们之前不创建任何promise,就像@Paulpro也建议的那样。 因此,您只需要编写一个严格的顺序函数即可
async function logData(id) {
const object = new SomeUsefulClassWhatTakesAdvantageOfMetadataProp();
object.metaData = await loadMetadata(id);
// Now metadata surely loaded, do something with it
console.log(object.metaData);
// But some singleData will be loaded only in future
object.singleData = await loadSingleData(object.metaData.dataToLoad);
console.log(object.singleData);
}
您的问题是startLoadingSingleData
职责过多。 它负责加载元数据和触发单数据加载。
您的logData
函数使用await startLoadingSingleData(id)
作为确保元数据可用的方法,这似乎不太直观。 尚不清楚startLoadingSingleData(id)
返回一个Promise,该Promise在元数据已加载时解析,并且对于编码人员第一次(或几个月后)第一次查看它会感到困惑。 代码应尽可能自我记录,这样您就不必用注释解释每一行。
我的建议是完全删除startLoadingSingleData
函数,然后在logData
内部执行此logData
:
async function logData(id) {
const metaData = await loadMetadata(id);
console.log(metaData);
const singleData = await loadSingleData(metaData.name);
console.log(singleData);
}
或者,如果您不希望logData await
SingleData承诺:
async function logData(id) {
const metaData = await loadMetadata(id);
console.log(metaData);
loadSingleData(metaData.name).then( singleData => {
console.log(singleData);
} );
}
如果您真的想继续使用函数startLoadingSingleData
那么我认为您应该使它返回一个包含两个Promises的数组或对象:
function startLoadingSingleData(id) {
const metaDataLoaded = loadMetadata(id);
const singleDataLoaded = metaDataLoaded.then(
metaData => loadSingleData(metaData.dataToLoad)
);
return { metaDataLoaded, singleDataLoaded };
}
然后,您的用法将类似于:
async function logData(id) {
const { metaDataLoaded, singleDataLoaded } = startLoadingSingleData(id);
const metaData = await metaDataLoaded;
console.log(metaData);
const singleData = await singleDataLoaded;
console.log(singleData);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.