Search code examples
javascriptjquerybootstrap-4single-threaded

Why is Bootstrap Spinner Unusable in Compute Intensive Context


I am aware Javascript is single-threaded. However I do not understand why the following code does not show/hide a spinner before/after a compute-intensive task.

Code outline:

showSpinner();
computeIntensiveTask();
hideSpinner();

The code (I am using the Bootstrap spinner)

const showSpinner = () => {
    const $spinner = $('#spacewalk-spinner').find('.spinner-border');
    $spinner.show();
};

const hideSpinner = () => {
    const $spinner = $('#spacewalk-spinner').find('.spinner-border');
    $spinner.hide();
};

The function computeIntensiveTask() does a ton of sqrt() and other 3D vector math.

When the code runs, the spinner never appears. Why is this happening?

UPDATE 0

As a test I tried simply updated the spinner elements color before/after:

before document.getElementById('spacewalk-spinner').style.color = 'rgb(255,0,0)';

after document.getElementById('spacewalk-spinner').style.color = 'rgb(0,255,0)';

and only the 'after' color change took place.

UPDATE 1

To clarify. If I remove the call to hideSpinner() and change showSpinner() to document.getElementById('spacewalk-spinner').style.display = 'block'. The spinner shows after computeIntensiveTask() completes. This despite the fact I have placed computeIntensiveTask() within call to window.setTimeout with a 500 ms wait.


Solution

  • You need to coordinate the updating of the UI by using a setTimeout function. Also you need to position the showSpinner and hideSpinner functions in relation to the updating of the UI. See this snippet as an example.

    const showSpinner = () => {
      const $spinner = $('#spacewalk-spinner').find('.spinner-border');
      $spinner.show();
      console.log('show');
    };
    
    const hideSpinner = () => {
      const $spinner = $('#spacewalk-spinner').find('.spinner-border');
      $spinner.hide();
      console.log('hide');
    };
    
    
    const computeIntensiveTask = () => {
      showSpinner();
      // begin the calculations after the UI updates by using setTimeout
      setTimeout(function() {
        for (var start = 1; start < 1000; start++) {
          // calculating...
          console.log('calc');
        }
        hideSpinner();
      }, 0);
    
    };
    
    computeIntensiveTask();
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="spacewalk-spinner">
      <div class="spinner-border" role="status">
        <span class="sr-only">Loading...</span>
      </div>
    </div>