Javascript 如何从异步函数返回承诺?

Javascript 如何从异步函数返回承诺?,javascript,es6-promise,Javascript,Es6 Promise,当我尝试从异步函数返回承诺时,无法区分返回的承诺和函数的状态 我认为,最简单的解决方案是将要返回的承诺放在一个数组中。下面是一个愚蠢的例子,但我希望它能说明问题所在: function loadMetaData(id) {/*...*/} // Returns Promise<MetaData> function loadSingleData(name) {/*...*/} // Returns Promise<SingleData> async function st

当我尝试从异步函数返回承诺时,无法区分返回的承诺和函数的状态


我认为,最简单的解决方案是将要返回的承诺放在一个数组中。下面是一个愚蠢的例子,但我希望它能说明问题所在:

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的元数据,然后等待一段时间,然后获取完整的singleData

但这有点老套

克服这种情况的预期方法是什么

附言: 这个问题也会出现,当我试图返回一个承诺,而这个承诺又能解决问题。

你的问题是惊人的加载SingleData有太多的责任。它负责加载元数据和触发加载singledata

logData函数使用await StartingsIngleDataId作为确保元数据可用的一种方法,这似乎不是很直观。startLoadingSingleDataid返回一个承诺并不明显,该承诺会在元数据加载时解析,对于第一次查看它的程序员或几个月后查看它的您自己来说,这将是相当混乱的。代码应该尽可能地自我记录,这样就不需要用注释解释每一行

我的建议是完全删除startingsingledata函数,只需在logData中执行此操作:

或者,如果您不希望logData等待SingleData承诺:

async function logData(id) {
    const metaData = await loadMetadata(id);
    console.log(metaData);

    loadSingleData(metaData.name).then( singleData => {
        console.log(singleData);
    } );
}
如果您真的想继续使用函数startingLoadingLeData,那么我认为您应该让它返回一个数组或一个包含两个承诺的对象:

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);
}

是的,不幸的是JS的承诺不是代数的,也不是逻辑的。除了不使用本机承诺和不使用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);
}

无法区分返回的承诺和函数的状态-我不知道这是什么意思。最简单的解决方案是将要返回的承诺放在数组中-异步函数必须返回承诺,而不是数组,即使该数组包含承诺。异步函数自动返回承诺。无论您从异步函数返回什么值,都将自动包装在承诺中。如果您确实执行异步函数{returnnewpromise…},那么您实际上是在返回一个Promise的Promise。除非这是你所需要的,否则你不需要自己创造承诺,只要回报你的价值。谢谢大家,更新。我希望现在这澄清了返回承诺的必要性,以及我的意思。好吧,我忘了:这就是a,b=getPromises;等待时机;等待b;。当loadMetaData拒绝时,您将从singleDataLoaded承诺中获得未处理的承诺拒绝:-
// 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);
}
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);
}