繁体   English   中英

如何从异步函数返回Promise?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM