EDIT: Not sure what I was thinking, maybe just too used to calling await at top-level. Firstly, you need to know why the while didn't work. Read here why while
is bad practice for the event loop here, and doesn't work at all: https://stackoverflow.com/a/61358589/17954209
The issue is that the while() loop runs until the flag changes value. As long as that while loop is running, the event loop is blocked. There's a setTimeout() that wants to fire in 1 second, but it can't actually call its callback until the interpreter gets back to the event loop.
Basically you never change the done value, because you are stuck inside the loop, and so it never can get changed, so you are stuck in the loop.
Anyways similar to the syntax you actually wanted you can just use a callback. Note that this means you handle the result explicitly within the functionThatCannotBeAsync
.
functionThatCannotBeAsync() {
const awaiting = someAsyncStuff() //This is a promise
awaiting.then((value) => {
//do stuff with your value,
return value
})
}
However this is non-thread blocking code!
//Somewhere else
console.log(typeof functionThatCannotBeAsync() === undefined)
//true <- Not what we expected!
99% of the time this is what you don't want.
Instead, we'll go with a callback, which is to use a callback function as a parameter. Note that type of value
is whatever you expect to receive back from your await function. Note that this is what async/await was created for, to prevent something called callback hell. It does mean wherever functionThatCannotBeAsync
, any dependent code on the awaited value must be inside the callback. You shouldn't have to worry about callback hell if it is only this function that cannot be async.
functionThatCannotBeAsync(callback: (value: any) => void) {
const awaiting = someAsyncStuff()
// awaited.then(callback)
// Or if you want to do stuff to the value first,
awaiting.then((value) => {
const newValue = value+'bar'
callback(newValue)
})
}
And then somewhere else
functionThatCannotBeAsync((value) => {
console.log("I am code dependent on value!")
//... do whatever with value
})
console.log("I am code not dependent on value!")
// logs:
// 'I am code not dependent on value!'
// 'I am code dependent on value!'
Finally the alternative is most similar to JayCodist's answer, only differing in that we can manipulate the result in our functionThatCannotBeAsync
then return a promise somewhere else to use await there instead.
function functionThatCannotBeAsyncPromise() {
const awaiting = someAsyncStuff() //This is a promise
const promise = new Promise((resolve, reject) => {
awaiting.then((value: string) => {
const newValue = value+'bar'
resolve(newValue)
}).catch((reason) => {
throw new Error(reason)
//or we can use reject
//reject(reason)
})
})
return promise
}
//...
//Somewhere else
const value = await functionThatCannotBeAsync();
Here is working examples on TS playground
Otherwise, some supplemental reading:
Original answer kept below for posterity, or something...
You can use an immediately invoked anonymous expression, IIFE, to convert your async function to sync.
An async IIFE allows you to use await and for-await even in older browsers and JavaScript runtimes that have no top-level await:
This is assuming you don't need to do anything between the time you call the IIFE and its return
functionThatCannotBeAsync() {
(async () => await someAsyncStuff())()
// wait for the above to finish
// return with result
}