Home > Software engineering >  Why function is executed although await is used?
Why function is executed although await is used?

Time:12-28

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

CodePudding user response:

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.

CodePudding user response:

Using the answer from How to make a promise from setTimeout, you can use a traditional loop.

function later(delay, value) {
  return new Promise(resolve => setTimeout(resolve, delay, value));
}

async function poll() {
  for (let count = 1;; count  ) {
    console.log(`Polling ${count}`);
    try {
      const data = Math.random(); // simulated data
      if (data < 0.2) { // simulated 20% success rate
        console.log("Poll", data)
        return data;
      } else {
        console.log("Retrying in 5 seconds");
        await later(5000);
      }
    } catch (err) {
      console.log(`${err}. Polling again in 5 seconds.`);
      count = 1;
      await later(5000);
    }
  }
}

async function main() {
  console.log("Start");
  await poll();
  console.log("Poll done");
}

main();

  • Related