Home > Net >  How can I create a simple loading animation while a function is running?
How can I create a simple loading animation while a function is running?

Time:03-28

I'm trying to create a simple loading animation for an arbitrary function in JQuery Terminal. I know that JavaScript is single threaded and synchronous, but are there any workarounds?

I've seen the JQuery progress bar animation example, but I'm not sure how I can run an animation similar to that while another function runs on the background. Could I potentially use async and await functions?

Below is a simple reproducible example:

var terminal = $('#terminal').terminal( function(command, term) {
                    arbitrary_function();
                    let i = 0;
                    let loading_animation = ['[LOADING     ]', '[LOADING .   ]', '[LOADING ..  ]', '[LOADING ... ]', '[LOADING ....]']
                    (function loop() {
                        terminal.set_prompt(loading_animation[i]);
                        timer = setTimeout(loop, 550);
                        i = ((i   1) % 5);
                    })();   
                }

Ideally, I'd want everything below arbitrary_function() to run until arbitrary_function() is done. How can I possibly do that in JavaScript?

CodePudding user response:

The problem here is how do you know the progress of your arbitary_function? It would have to update the "main process" with it's status. You can run arbitrary_function async by using setTimeout

In the example below the arbitary_function executes itself in a loop via setTimeout with random delays and updates progress variable with each execution. Meanwhile showProgress function runs in a loop independently from arbitrary_function and displays data from progress variable.

const elProgress = document.querySelector(".progress-bar > .progress");

let iProgress = 0; //this will hold the progress of our function
function arbitary_function()
{
  iProgress  = Math.random() * 10;
  if (iProgress < 100)
    return setTimeout(arbitary_function, Math.random() * 500);

  iProgress = 100;
}

arbitary_function();


function showProgress(timestamp)
{
  elProgress.textContent = ~~iProgress   "%";
  elProgress.style.width = iProgress   "%";
  if (iProgress < 100)
    requestAnimationFrame(showProgress);
}

showProgress();
.progress-bar
{
  border: 1px solid black;
  height: 3em;
  border-radius: 3em;
  overflow: hidden;
}
.progress-bar > .progress
{
  background-color: red;
  height: 100%;
  border-right: 1px solid gray;
  transition: width 0.5s;
  line-height: 3em;
  text-align: center;
  vertical-align: middle;
  overflow: hidden;
}
<div >
  <div ></div>
</div>

CodePudding user response:

@Wyck is right you can use Workers for this if your arbitrary_function do long computation, here is POC for this using Comlink library

// function to worker
function fworker(fn) {
    var str = '('   fn.toString()   ')()';
    // ref: https://stackoverflow.com/a/10372280/387194
    var URL = window.URL || window.webkitURL;
    var blob;
    try {
        blob = new Blob([str], { type: 'application/javascript' });
    } catch (e) { // Backwards-compatibility
        const BlobBuilder = window.BlobBuilder ||
              window.WebKitBlobBuilder ||
              window.MozBlobBuilder;
        blob = new BlobBuilder();
        blob.append(str);
        blob = blob.getBlob();
    }
    return new Worker(URL.createObjectURL(blob));
}

const worker = Comlink.wrap(fworker(function() {
  importScripts("https://cdn.jsdelivr.net/npm/comlink/dist/umd/comlink.min.js");

  Comlink.expose({
    longTask() {
      let i = 4000000000;
      while (i--) {
        Math.pow(2, 100000000);
        Math.pow(2, 100000000);
        Math.pow(2, 100000000);
        Math.pow(2, 100000000);
      }
    }
  });
}));

function arbitrary_function() {
  return worker.longTask();
}

var terminal = $('body').terminal(function() {
  const promise = arbitrary_function();
  // we don't want interactivity, but want prompt to be visible
  terminal.pause(true);
  let i = 0;
  const loading_animation = [
    '[LOADING     ]',
    '[LOADING .   ]',
    '[LOADING ..  ]',
    '[LOADING ... ]',
    '[LOADING ....]'
  ];
  let stop = false;
  const prompt = terminal.get_prompt();
  (function loop() {
      terminal.set_prompt(loading_animation[i]);
      if (!stop) {
          timer = setTimeout(loop, 550);
      } else {
          // clear the animation
          terminal.echo(loading_animation[i]).set_prompt(prompt);
      }
      i = ((i   1) % 5);
  })();
  promise.then(function() {
    // long calculation is done
    stop = true;
    terminal.resume();
  });
});
<script src="https://cdn.jsdelivr.net/npm/comlink/dist/umd/comlink.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery.terminal/js/jquery.terminal.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/jquery.terminal/css/jquery.terminal.css" rel="stylesheet"/>

  • Related