Why function is executed although await is used?



I have used await keyword in the main function to wait for the completion of async function call to poll() and yet the function call to my_plot is made before the completion of the poll() function.

async function main() {
    getParametersData()
    await poll()
    my_plot()
} 
async function getData() {
    const response = await fetch(API)
    const message = await response.json()
    return message
}

async function poll(count = 1) {
    console.log(`Polling ${count}`);
    try {
        const data = await getData();
        if (data && Object.keys(data).length !== 0) {
            console.log("Poll", data)
            return;
        } else {
            setTimeout(poll, 5000, ++count);
        }
    } 
    catch (err) {
        console.log(`${err}. Polling again in 5 seconds.`);
        setTimeout(poll, 5000, 1);
    }

}

async function my_plot() {
    console.log("my plot")
}

Code output:

Polling 1
my plot 
Polling 2
Polling 3
Poll [1,2,3]

Expected:

Polling 1
Polling 2
Polling 3
Poll [1,2,3]
my plot

Answer

Don’t use setTimeout directly from within an async function. Instead, use a Promise-based wrapper.

It’s surprising that modern ECMAScript doesn’t come with an in-box Promise-based version of setTimeout, but it’s straightforward to implement:

function delay( timeout ) {
    if( typeof timeout !== 'number' || timeout < 0 ) throw new Error( "Timeout must be a non-negative integer milliseconds delay value." );

   return new Promise( function( resolve ) { 
       setTimeout( resolve, timeout );
   });
}
  • Then you can rewrite your poll function with a “real” while loop, like so (below).
  • I think your poll function should return a true/false value to indicate success or failure to the caller, if you ever need to.
  • Consider using typeof instead of less safe checks like Object.keys(data).length – or at least using a typeof check before using Object.keys.
    • Though annoyingly typeof null === 'object', so you will always need a !== null check, grumble
    • As an alternative, consider having your own type-guard function (yes, I know this isn’t TypeScript), that way you get even stronger guarantees that data contains what you need (as JS does not have static type checking).
async function poll( count = 1 ) {
    
    console.log(`Polling ${count}`);
 
    let i = 0;
    do {
        try {
            const data = await getData();
            if( isMyData( data ) ) {
                return true;
            }
        }
        catch( err ) {
            console.error( err );
        }

        console.log( "Polling again in 5 seconds." );
        await delay( 5000 );

        i++;
    }
    while( i < count );

    console.log( `Gave up after ${count} attempts.` );
    return false;
}

// Type-guard:
function isMyData( data ) {
    
    return (
        ( typeof data === 'object' )
        &&
        ( data !== null )
        &&
        ( 'this is my object' in data )
        &&
        ( data['there are many like it but this one is mine'] )
        &&
        ( data.myJavaScriptEngineIsMyBestFriend )
        &&
        data.itIsMyLife
        &&
        data.withoutMe_javaScriptIsUseless
        &&
        data.withoutJavaScript_iAmUseLess > 0
    );
}

Note that if you intend to catch errors thrown by getData you should use a minimally scoped try instead of having more logic in there, as generally you won’t want to catch unrelated errors.



Source: stackoverflow