Estoy tratando de crear una animación de carga simple para una función arbitraria en JQuery Terminal. Sé que JavaScript es de un solo subproceso y síncrono, pero ¿hay alguna solución?
He visto el ejemplo de animación de la barra de progreso de JQuery , pero no estoy seguro de cómo puedo ejecutar una animación similar a esa mientras otra función se ejecuta en segundo plano. ¿Podría potencialmente usar funciones asíncronas y de espera?
A continuación se muestra un ejemplo reproducible simple:
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); })(); }Idealmente, me gustaría que todo debajo de arbitrary_function() se ejecute hasta que se complete arbitrary_function(). ¿Cómo puedo hacer eso en JavaScript?
@Wyck tiene razón, puede usar Web Workers para esto si su función arbitraria hace un cálculo largo, aquí hay un ejemplo de cómo hacer esto usando la biblioteca Comlink
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) .resume(); } i = ((i + 1) % 5); })(); promise.then(function() { // long calculation is done stop = true; }); }); function arbitrary_function() { return worker.longTask(); } const worker = Comlink.wrap(fworker(function() { importScripts("https://cdn.jsdelivr.net/npm/comlink/dist/umd/comlink.min.js"); Comlink.expose({ longTask() { let i = 3000000000; while (i--) { Math.pow(2, 100000000); } } }); })); // 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)); } <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"/>NOTA : Si su función es simplemente asíncrona y no bloquea el hilo principal, puede usar Promises y no necesita Web Worker para eso.
function arbitrary_function() { return new Promise(resolve => { setTimeout(resolve, 1000); }); }En lugar de setTimeout, puede usar cualquier función. El resto del código será el mismo (pero sin Web Worker).
El problema aquí es ¿cómo sabes el progreso de tu arbitary_function ? Tendría que actualizar el "proceso principal" con su estado. Puede ejecutar arbitrary_function async usando setTimeout
En el siguiente ejemplo, la arbitary_function se ejecuta en un bucle a través de setTimeout con retrasos aleatorios y actualiza la variable de progress con cada ejecución. Mientras tanto, la función showProgress se ejecuta en un bucle independientemente de arbitrary_function y muestra datos de la variable de progress .
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 class="progress-bar"> <div class="progress"></div> </div>