georgej
georgej

Reputation: 3311

Pure CSS spinner freezes

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

Answers (1)

Brandon Gano
Brandon Gano

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

Related Questions