Skip to content Skip to sidebar Skip to footer

How To Return A Promise From Async Function?

When I try to return a promise from an async function, it's impossible to distinguish the status of the returned Promise and the function. I think, the simplest solution is to put

Solution 1:

Yes, unfortunately JS promises are not algebraic and you cannot fulfill a promise with another promise. There's no way around that (other than not using native promises, and not using async/await).

The easiest and most common solution is indeed using a wrapper object. It comes naturally to your problem:

// takes an id, returns a Promise<{metaData: Data, singleDataPromise: Promise<Data>}>asyncfunctionstartLoadingSingleData(id) {
    constobject = newSomeUsefulClassWhatTakesAdvantageOfMetadataProp();
    object.metaData = awaitloadMetadata(id);
//                    ^^^^^object.singleDataPromise =   loadSingleData(object.metaData.dataToLoad);
//                             ^ no await herereturnobject;
}
asyncfunctionlogData(id) {
    constobject = awaitstartLoadingSingleData(id));
    // Now metadata surely loaded, do something with itconsole.log(object.metaData);
    // But some singleData will be loaded only in futureconst singleData = awaitobject.singleDataPromise;
    console.log(singleData);
}

Notice that this potentially leads to problems with unhandled rejections if there is an exception in your code and you never get to await the singleDataPromise.

The (probably much better) alternative is to restructure your functions so that you don't create any promises before using (i.e. awaiting) them, like @Paulpro also suggested. So you'd just write a single strictly sequential function

asyncfunctionlogData(id) {
    constobject = newSomeUsefulClassWhatTakesAdvantageOfMetadataProp();
    object.metaData = awaitloadMetadata(id);
    // Now metadata surely loaded, do something with itconsole.log(object.metaData);
    // But some singleData will be loaded only in futureobject.singleData = awaitloadSingleData(object.metaData.dataToLoad);
    console.log(object.singleData);
}

Solution 2:

Your problem is that startLoadingSingleData has too many responsibilities. It is responsible for both loading the metadata and triggering loading of singledata.

Your logData function uses await startLoadingSingleData(id) as a way to make sure that metadata is available, which does not seem very intuituve. It is not obvious that startLoadingSingleData(id) returns a Promise that resolves when the metadata has loaded, and would be quite confusing for a coder looking at it for the first time (or yourself after a few months). Code should be self-documenting as much as possible so that you don't need to explain every line with comments.

My recommendation is to completely remove the startLoadingSingleData function and just do this inside logData:

asyncfunctionlogData(id) {
    const metaData = awaitloadMetadata(id);
    console.log(metaData);

    const singleData = awaitloadSingleData(metaData.name);
    console.log(singleData);
}

or if you don't want logData to await the SingleData Promise:

asyncfunctionlogData(id) {
    const metaData = awaitloadMetadata(id);
    console.log(metaData);

    loadSingleData(metaData.name).then( singleData => {
        console.log(singleData);
    } );
}

If you really want to keep using the function startLoadingSingleData instead then I think you should make it return an array or an object containing two Promises:

functionstartLoadingSingleData(id) {
    const metaDataLoaded = loadMetadata(id);
    const singleDataLoaded = metaDataLoaded.then(
      metaData =>loadSingleData(metaData.dataToLoad)
    );

    return { metaDataLoaded, singleDataLoaded };
}

Then your usage would look something like:

asyncfunctionlogData(id) {
    const { metaDataLoaded, singleDataLoaded } = startLoadingSingleData(id);

    const metaData = await metaDataLoaded;
    console.log(metaData);

    const singleData = await singleDataLoaded;
    console.log(singleData);
}

Post a Comment for "How To Return A Promise From Async Function?"