0

I have this case below (for simplicity, the functions has no, or a little bit of, content):

async someAsyncStuff() {
    let result = await stuffToDo()
    return result
}

functionThatCannotBeAsync() {
   someAsyncStuff()
   // wait for the above to finish
   // return with result
}

Now for system-wise reasons, functionThatCannotBeAsync() cannot be async and therefore cannot use await someAsyncStuff(), but it still needs to call it, wait for it to finish, and then return with its result.

I've tried to come up with something like this:

async someAsyncStuff(res, callback) {
    let result = await stuffToDo()
    
    res.result = result
    callback()
    return result
}

functionThatCannotBeAsync() {
   let done = false
   let result = {result: ""}

   someAsyncStuff(result, () => { done = true })

   while(!done) {}
   return result.result
}

My logic says that functionThatCannotBeAsync() should've waited in the while loop, and eventually done will become true and the function will return with the desired value. In actual, the while loop lasts forever.

Can someone please explain to me why this is happening? What is the correct way to do it (except for async\await, which I cannot use here).

Thanks in advance

DanielY
  • 1,141
  • 30
  • 58
  • 1
    "My logic says that functionThatCannotBeAsync() should've waited in the while loop" - blocking the whole system and preventing the async work from being done. – Cerberus Mar 23 '22 at 06:19
  • @Cerberus I agree that a small delay in the while should've been added to free the system for others things, but there's no delay() or sleep() in JS!!! – DanielY Mar 23 '22 at 06:21

2 Answers2

1

You can implicitly return a Promise. Like so:

/* To implement a function that fetches from an endpoint and returns a promise of the result without using `async`/`await` syntax
*/
function functionThatCannotBeAsync(url) {

  return new Promise(resolve => {
     fetch(url)
        .then(response => response.json())
        .then(jsonResponse => resolve(jsonResponse));
  })
}

const clientFunction = async () => {
 const apiResponse = await functionThatCannotBeAsync("http://api.plos.org/search?q=title:DNA");
 console.log("response: ", apiResponse);
}
clientFunction();
JayCodist
  • 2,424
  • 2
  • 12
  • 28
0

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
}
Cody Duong
  • 2,292
  • 4
  • 18
  • this will reach the next line (the one commented with //wait for the above to finish) before the above is finished – DanielY Mar 24 '22 at 05:16
  • @DanielY oh yup, my bad. I went ahead and updated my answer with a thorough explanation of the various ways you could solve this, go ahead and lmk if this works for you. – Cody Duong Mar 24 '22 at 08:41