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"/>