5

I want to preface by saying I've viewed a lot of stackoverflow questions regarding this topic, but I haven't found any 'duplicates' per se since none of them contain solutions that would solve this specific case.

I've mainly looked at How do I return the response from an asynchronous call?, and the section on 'Promises with async/await' would work inside an asynchronous function, but the function I'm working on is not async and is part of an existing codebase that I can't change easily. I wouldn't be able to change the function to async.

The section on callbacks wouldn't work either, which is explained further below. Anyway, I'll jump into my question:

I'm editing a function (standard function, not async) in JavaScript by adding an asynchronous function call. I want to wait until the async call finishes before I return from the function (since the result of the async call needs to be included in the return value). How would I do so?

I looked into using callbacks, which would allow me to write code which is guaranteed to run only after the async call completes. However, this wouldn't interrupt the flow of the program in the original function, and the original function could still return before the callback is run. A callback would allow me to execute something sequentially after the async function, but it wouldn't allow me to wait for asynchronous call to complete at the highest level.

Example code, which wouldn't return the desired result:

function getPlayers() {
    ... other code ...

    let outfieldPlayers = asyncGetOutfieldPlayersCall()

    ... other code ...

    allPlayers.add(outfieldPlayers)

    return allPlayers  // the returned value may or may not include outfield players
}

The actual problem I'm facing is a little more complicated - I'm calling the async function in each iteration of a for loop, and need to wait until all calls have completed before returning. But, I think if I can solve this simpler problem, I can solve the problem with a for loop.

Bob Dole
  • 345
  • 1
  • 3
  • 11
  • 1
    you haven't provided much code that is useful enough to help, but ... outfeldPlayers will be a Promise (if by "asyncGetOutfieldPlayersCall" is a true `async function`) so you can't just add outfieldPlayers to allPlayers, unless this `.add` method expects a Promise – Jaromanda X Apr 23 '20 at 00:37
  • ah yeah that's right. the code I provided wouldn't actually work because as you said, the async function call would return a promise, not a list/etc. the above example is how I'd want the function to behave if I could wait for the async call to succeed, which I don't know how to do now. (assuming that if the promise succeeds, it returns a list) – Bob Dole Apr 23 '20 at 00:40
  • 4
    You can't magically turn something asynchronous into a synchronous flow, that would essentially require time travel. You're just going to have to adapt your code to this reality and make everything that depends on the result of the async process also be async. There's no way around this in JS since it's single-threaded. – Lennholm Apr 23 '20 at 00:41

2 Answers2

2

Sadly, it is pretty much impossible to wait for async code in a synchronous way. This is because there is no threading in JS (most JS runtimes, but some are). So code is either synchronous or asynchronous.

Asynchronous code is possible because of the event loop. The event loop is part of the javascript runtime. It works by keeping a stack of callback functions that run when events trigger them - usually either timeout events (which you can set with setTimeout()) or IO events (which happen when you make disk or HTTP requests, or on user interaction). However, these callbacks only run when no other code is running, so only when the program is idle and all functions have returned.

This means that techniques like "spin loops" (where you just run a loop until a condition is changed by another thread) that work in threaded environments don't work because the async code won't run until the spin loop finishes.

More Info: https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

Garrett Motzner
  • 3,021
  • 1
  • 13
  • 30
2

If you are using NodeJS, this is possible through execSync.

This requires you to place your asynchronous code in a separate file, spawn a separate process using execSync, which will wait until it exits.

For example, consider the following async function which prints an array.

// task.js

(async function() {
    await new Promise((resolve) => setTimeout(() => {
        console.log(JSON.stringify([3,4,5]));

        resolve();
    }, 1000));
})();

Now, you can invoke this from your main process:

function asyncGetOutfieldPlayersCall() {
    const execSync = require('child_process').execSync;

    return JSON.parse(execSync("node task.js"));
}

function getPlayers() {
    let allPlayers = [1,2];
    // ... other code ...

    let outfieldPlayers = asyncGetOutfieldPlayersCall();

    // ... other code ...

    allPlayers = allPlayers.concat(outfieldPlayers)

    return allPlayers;
}
dinesh ygv
  • 1,840
  • 1
  • 14
  • 18