当我尝试从异步函数返回一个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(/*...*/)
时,请在短时间后首先获取给定数据的给定ID的metaData
,等待一段时间后,预计会显示完整的singleData
。
但这有点像个hackish。
克服这种情况的目的是什么?
PS .: 当我尝试返回一个以promise为结果的Promise时,也会出现此问题。
答案 0 :(得分:3)
是的,遗憾的是JS承诺不是代数和you cannot fulfill a promise with another 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);
}
请注意,如果您的代码中存在异常且您永远无法await
singleDataPromise
,则可能会导致未处理的拒绝问题。
(可能更好)替代方案是重构您的功能,以便在使用(即等待)它们之前不会创建任何承诺,如@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);
}
答案 1 :(得分:1)
你的问题是startLoadingSingleData
责任太多了。它负责加载元数据和触发singledata的加载。
您的logData
函数使用await startLoadingSingleData(id)
作为确保元数据可用的方法,这似乎不太直观。 startLoadingSingleData(id)
返回一个在元数据加载时解析的Promise并不明显,对于第一次看到它(或者几个月后你自己)的编码器来说会很困惑。代码应该尽可能自我记录,这样您就不需要用注释解释每一行。
我的建议是完全删除startLoadingSingleData
功能,并在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 Promise:
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);
}