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 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.
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();