Reputation: 3311
I have a pure CSS spinner that I want to show while my JavaScript is doing other work (an async call and then iterating through an array of about 10,000 elements). The spinner appears at the right time but freezes when a certain point in the code is hit. In the code below, the console.log
s for the 'one', 'two', and 'three' happen basically instantly - then there is a long pause (which is when my spinner freezes) then 'four' and 'five' log out together and my content loads. So basically I know that the code between the "console.log('three')
" and "console.log('four')
":
// for search bar
search(text) {
this.searching = true; // shows spinner
console.log('one');
var text = text.toUpperCase();
// search the text with an api call
this.securitiesService.searchSecurities(text)
.subscribe(data => {
var response = data.response;
console.log('two');
// if no search results are present go to correct page
if (!response.length) {
this.router.navigate(...to route...);
return;
}
console.log('three');
// FREEZES HERE !!!
for (var i = 0; i < response.length; i++) {
if (text === response[i].ticker) {
// UNFREEZES HERE !!!
console.log('four');
this.router.navigate(...to route...);
this.searching = false;
console.log('five');
return;
}
}
})
}
This happens with every pure CSS spinner I try.
Am I wrong in thinking that my JavaScript shouldn't freeze my pure CSS spinner? Why would the code freeze it?
Upvotes: 2
Views: 930
Reputation: 6710
JavaScript runs on the UI thread, so it will still block CSS-only spinners. You need to move the work off the UI thread or split it up so the UI has time to update.
Depending on which browsers you need to support, you might be able to use Web Workers. See http://caniuse.com/#search=web%20workers for browser support or http://www.html5hacks.com/blog/2012/09/22/web-workers-basics-of-the-web-browsers-ui-thread/ for examples.
You can also process one (or 10 or 100) records at a time and call each "batch" within window.setTimeout
or window.requestAnimationFrame
. Try something like this (code not tested):
// for search bar
search(text) {
this.searching = true; // shows spinner
console.log('one');
var text = text.toUpperCase();
// search the text with an api call
this.securitiesService.searchSecurities(text)
.subscribe(data => {
var response = data.response;
console.log('two');
// if no search results are present go to correct page
if (!response.length) {
this.router.navigate(...to route...);
return;
}
console.log('three');
var i = 0;
var self = this;
var processBatch = function () {
if (text === response[i].ticker) {
console.log('four');
self.router.navigate(...to route...);
self.searching = false;
console.log('five');
return;
}
if (++i < response.length) {
window.setTimeout(processBatch, 0);
}
};
window.setTimeout(processBatch, 0);
});
}
If you want to process more than one item per batch, you can have a loop within processBatch
that iterates through a small number of items.
Side note: See @gelliott181's comment above if data.response
is a string.
Upvotes: 4