简体   繁体   中英

Can you await an async member function in another async member function?

I've got a class that does something like the following:

class Loader
{
    async load()
    {
        let pending = [...];
        return new Promise( (resolve, reject) => 
        {
            while(pending.length > 0)
            {
                await this._loadAsset(pending.pop());
            }
            resolve();
        });
    }

    async _loadAsset(asset)
    {
        return new Promise( (resolve, reject) =>
        {
            // Loading logic goes here, eventually calling...
            resolve();
        });
    }
}

( pending is an array of objects that I'm too lazy to list out here.)

The problem I'm having is that Chrome is giving me an Uncaught SyntaxError: await is only valid in async function error when I try to load my script. As far as I can tell, Loader's load and _loadAsset methods have both been declared async. What am I missing? Is the while loop complicating things?

The callback function is not declared async , so you could try adding async like:

return new Promise( async (resolve, reject) => 
  {
    while(pending.length > 0) {
      await this._loadAsset(pending.pop());
  }
  resolve();
});

But an even better option for resolving a lot of pending promised is the Promise.all() function

You can simplify your load function to:

async load()
{
    let pending = [...];
    while(pending.length > 0)
    {
        await this._loadAsset(pending.pop());
    }
}

Theres no need for the inner promise construction.

You could also do this:

load()
{
    let pending = [...];
    return Promise.all(pending.map(i => this._loadAsset(i)));
}

The problem is that the anonymous function you're passing to the Promise constructor uses await but is not declared async :

return new Promise( (resolve, reject) =>  // this function uses await but is not async
    {
        while(pending.length > 0)
        {
            await this._loadAsset(pending.pop());
        }
        resolve();
    });

Generally you won't need to use the Promise constructor if you're not wrapping something that takes a callback. In this case, you don't need it at all, you can just have the while loop be part of load 's body:

async load()
{
    let pending = [/*...*/];
    while(pending.length > 0)
    {
        await this._loadAsset(pending.pop());
    }
}

You should not return a promise from an async function. That's because under the hood, any value returned from async functions will be wrapped into a promise:

// The following function
async function foo() {
    await someAsyncFunction();
    return "done";
}

// Is the same as
function bar() {
    return new Promise((resolve) => {
        someAsyncFunction().then(() => resolve("done"));
    });
}

So your code should be rewritten to:

class Loader
{
    async load()
    {
        let pending = [...];

        while(pending.length > 0)
        {
            await this._loadAsset(pending.pop());
        }
    }

    async _loadAsset(asset)
    {
        // Loading logic happens here, but instead of calling resolve(), just return. Remember to add await to any async operation.
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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