如何在函数为 运行 时创建简单的加载动画?

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

我正在尝试为 JQuery 终端中的任意函数创建一个简单的加载动画。我知道 JavaScript 是单线程和同步的,但是有什么解决方法吗?

我看过 JQuery progress bar animation example,但我不确定如何 运行 类似的动画,而另一个功能 运行 在背景上。我可以使用 async 和 await 函数吗?

下面是一个简单的可重现示例:

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);
                    })();   
                }

理想情况下,我想要 arbitrary_function() 到 运行 下的所有内容,直到 arbitrary_function() 完成。我怎么可能在 JavaScript 中做到这一点?

这里的问题是你怎么知道你的arbitary_function的进度?它必须用它的状态更新“主进程”。 您可以 运行 arbitrary_function 使用 setTimeout

异步

在下面的示例中,arbitary_function 通过 setTimeout 在循环中执行自身,具有随机延迟并在每次执行时更新 progress 变量。同时 showProgress 函数 运行 独立于 arbitrary_function 循环并显示来自 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>

@Wyck 是对的,如果您的 arbitrary_function 进行长时间计算,您可以为此使用 Web Workers,这里是如何使用 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: 
    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"/>

注意:如果您的函数只是异步的并且不会阻塞主线程,您可以只使用 Promises 而不需要 Web Worker。

function arbitrary_function() {
    return new Promise(resolve => {
        setTimeout(resolve, 1000);
    });
}

除了setTimeout,您可以使用任何函数。其余代码将相同(但没有 Web Worker)。