Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript - wait for async call to finish before returning from function, without the use of callbacks

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.

like image 435
Bob Dole Avatar asked Apr 23 '20 00:04

Bob Dole


2 Answers

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

like image 200
Garrett Motzner Avatar answered Oct 20 '22 01:10

Garrett Motzner


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;
}
like image 2
dinesh ygv Avatar answered Oct 20 '22 01:10

dinesh ygv