如何创建自己的 setTimeout 函数?

Đin*_*Huy 6 javascript asynchronous

我了解如何使用setTimeout函数,但我找不到创建类似函数的方法。
我有一个例子:

setTimeout(() => {
  console.log('3s');
}, 3000);
while(1);
Run Code Online (Sandbox Code Playgroud)

结果是setTimeout回调永远不会调用,所以我认为它像每个 js 其他函数一样使用相同的线程。但是当它检查时间是否到达时?以及它如何做到这一点?

更新

为了避免误解,我更新了我的问题。
我找不到在指定时间后创建带有回调的异步函数的方法(不使用setTimeout也不阻塞整个线程)。这个功能setTimeout对我来说就像一个奇迹。我想了解它是如何工作的。

Kai*_*ido 8

只是为了游戏,因为我真的不明白为什么你不能使用 setTimeout ......


要创建非阻塞计时器,而不使用 setTimeout/setInterval 方法,您只有两种方法:

  • 基于事件的计时器
  • 在第二个线程中运行无限循环

基于事件的计时器

一种简单的实现是使用MessageEvent接口并轮询直到时间已到。但这对于长时间的超时来说并不是真正的建议......

function myTimer(cb, ms) {
  var now = performance.now();
  window.addEventListener('message', handleMessage);
  postMessage('myTimer', '*');
  function handleMessage(evt) {
   if(evt.data === 'myTimer') {
      if(performance.now() - now >= ms) {
        window.removeEventListener('message', handleMessage);
        cb();
      }
      else {
        postMessage('myTimer', '*');
      }
    }
  }

}
myTimer(()=>console.log('world'), 2000);
myTimer(()=>console.log('hello'), 200);
Run Code Online (Sandbox Code Playgroud)

因此,如果可用,人们可能希望使用Web Audio APIAudioScheduledSourceNode,它们充分利用了高精度音频上下文自己的时钟:

function myTimer(cb, ms) {
  if(!myTimer.ctx) myTimer.ctx = new (window.AudioContext || window.webkitAudioContext)();
  var ctx = myTimer.ctx;
  var silence = ctx.createGain();
  silence.gain.value = 0;
  var note = ctx.createOscillator();
  note.connect(silence);
  silence.connect(ctx.destination);
  note.onended = function() { cb() };
  note.start(0);
  note.stop(ctx.currentTime + (ms / 1000));
}

myTimer(()=>console.log('world'), 2000);
myTimer(()=>console.log('hello'), 200);
Run Code Online (Sandbox Code Playgroud)

不同线程上的无限循环

是的,使用Web Workers我们可以运行无限循环而不会杀死我们的网页:

function myTimer(cb, ms) {
  var workerBlob = new Blob([mytimerworkerscript.textContent], {type: 'application/javascript'});
  var url = URL.createObjectURL(workerBlob);
  var worker = new Worker(url);
  worker.onmessage = function() {
    URL.revokeObjectURL(url);
    worker.terminate();
    cb();
  };
  worker.postMessage(ms);
}

myTimer(()=>console.log('world'), 2000);
myTimer(()=>console.log('hello'), 200);
Run Code Online (Sandbox Code Playgroud)
<script id="mytimerworkerscript" type="application/worker-script">
  self.onmessage = function(evt) {
    var ms = evt.data;
    var now = performance.now();
    while(performance.now() - now < ms) {}
    self.postMessage('done');
  }
</script>
Run Code Online (Sandbox Code Playgroud)