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
Advertisement
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 atrue
/false
value to indicate success or failure to the caller, if you ever need to. - Consider using
typeof
instead of less safe checks likeObject.keys(data).length
– or at least using atypeof
check before usingObject.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).
- Though annoyingly
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.