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?
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.
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.
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>