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.
JavaScript
x
33
33
1
async function main() {
2
getParametersData()
3
await poll()
4
my_plot()
5
}
6
async function getData() {
7
const response = await fetch(API)
8
const message = await response.json()
9
return message
10
}
11
12
async function poll(count = 1) {
13
console.log(`Polling ${count}`);
14
try {
15
const data = await getData();
16
if (data && Object.keys(data).length !== 0) {
17
console.log("Poll", data)
18
return;
19
} else {
20
setTimeout(poll, 5000, ++count);
21
}
22
}
23
catch (err) {
24
console.log(`${err}. Polling again in 5 seconds.`);
25
setTimeout(poll, 5000, 1);
26
}
27
28
}
29
30
async function my_plot() {
31
console.log("my plot")
32
}
33
Code output:
JavaScript
1
6
1
Polling 1
2
my plot
3
Polling 2
4
Polling 3
5
Poll [1,2,3]
6
Expected:
JavaScript
1
6
1
Polling 1
2
Polling 2
3
Polling 3
4
Poll [1,2,3]
5
my plot
6
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:
JavaScript
1
8
1
function delay( timeout ) {
2
if( typeof timeout !== 'number' || timeout < 0 ) throw new Error( "Timeout must be a non-negative integer milliseconds delay value." );
3
4
return new Promise( function( resolve ) {
5
setTimeout( resolve, timeout );
6
});
7
}
8
- 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
JavaScript
1
49
49
1
async function poll( count = 1 ) {
2
3
console.log(`Polling ${count}`);
4
5
let i = 0;
6
do {
7
try {
8
const data = await getData();
9
if( isMyData( data ) ) {
10
return true;
11
}
12
}
13
catch( err ) {
14
console.error( err );
15
}
16
17
console.log( "Polling again in 5 seconds." );
18
await delay( 5000 );
19
20
i++;
21
}
22
while( i < count );
23
24
console.log( `Gave up after ${count} attempts.` );
25
return false;
26
}
27
28
// Type-guard:
29
function isMyData( data ) {
30
31
return (
32
( typeof data === 'object' )
33
&&
34
( data !== null )
35
&&
36
( 'this is my object' in data )
37
&&
38
( data['there are many like it but this one is mine'] )
39
&&
40
( data.myJavaScriptEngineIsMyBestFriend )
41
&&
42
data.itIsMyLife
43
&&
44
data.withoutMe_javaScriptIsUseless
45
&&
46
data.withoutJavaScript_iAmUseLess > 0
47
);
48
}
49
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.