0

I have a project consisting out of thousands of lines of Javascript code. It contains a lot of complex calculations which are divided into different functions. One function calls three or four other functions, of which one calls another one of those four functions,... Just to say, it has a complex structure.

Some functions also need to do calculations on the DOM. E.g, One functions adds an element to the DOM, and another one needs to do a calculation using the coordinates of that element in the DOM.

However, when the user visits the page, all of this is executed by one function called - in this example - bigFunction.

The problem is that bigFunction starts a long series of complex calculations and function calls resulting in stalling the page loading, and making the page irresponsive until bigFunction is finished.

I already tried simple solutions like defering my .js-file, using $(document).ready(function() { bigFunction(); }), $(window).on('load', function() { bigFunction(); }). None of that changed anything. I tried converting my code to async code but that was crazy work.

I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?

Some test-code:

// Executing big function
bigFunction();
// "HTML"
console.log("[+] HTML Loaded!!"); // -> Must load async with bigFunction running

// This is a function I execute on a page and which stops the page from
// loading until it's finished. How do I async this?
async function bigFunction() {
    console.log("[+] bigFunction started");

    if (functionToReturnTrueOrFalseAfterALongCalculation()) {
        console.log("WOW!");
    }

    console.log("[+] bigFunction finished")
}



// Ignore logic in this function... It just returns true of false randomly after 3,5 secs
function functionToReturnTrueOrFalseAfterALongCalculation() {
    console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation started");

    let to_return = false;
    // Give back random value
    let random_number = Math.floor(Math.random() * 100) + 1;
    if (random_number < 50) {
        to_return = true;
    }

    sleep(3500);

    // Just to show that functions are nested deep in each other
    doRandomStuff();

    console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation ended");
    return to_return;
}

// Just another slow function
function doRandomStuff() {
    console.log("[+] doRandomStuff started");

    sleep(1000);

    console.log("[+] doRandomStuff ended");
}


// Ignore
function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}

HTML should load faster...

1
  • javascript is mainly single threaded - functionToReturnTrueOrFalseAfterALongCalculation looks cpu bound - and not needed to be async. note that your sleep is very poor. Commented Aug 15, 2021 at 0:51

1 Answer 1

1

I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?

Fundamentally, not really. In order for the page to remain responsive, your JavaScript code must yield control back to the browser regularly. If you have some very expensive JS that blocks for 0.5 seconds, the page will appear to the user to be unresponsive for those 0.5 seconds. (0.5 seconds of blocking is not good. 0.05 seconds of blocking every 0.1 seconds could be better, for example.)

To have the site remain responsive, all of your functions that invoke sleep (the expensive calculations) need to be refactored to stagger up their jobs. Pure JavaScript jobs can be offloaded to a service worker, thereby freeing up resources to keep the active tab responsive, but jobs that require DOM manipulation (some of which you have) can't be offloaded to a service worker.

Without seeing what exactly the expensive functions are doing, more details are difficult, but for what you want, in the end you'll probably need to end up with code that looks something like

async function bigFunction() {
    console.log("[+] bigFunction started");
    // the below function does not block for long periods of time,
    // but only for short periods of time, staggered up
    const result = await functionToReturnTrueOrFalseAfterALongCalculation();
    if (result) {
        console.log("WOW!");
    }

    console.log("[+] bigFunction finished")
}

The code that comes after the execution of bigFunction would absolutely have to be able to handle the asynchronous execution flow - if you had

doSomething();
bigFunction();
finishDoingStuff();

you would have to refactor to something like

doSomething();
await bigFunction();
finishDoingStuff();

or to

doSomething();
bigFunction(finishDoingStuff);

where bigFunction calls finishDoingStuff once its expensive tasks are complete.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.