javascript - 如何创建自己的 setTimeout 函数?

标签 javascript asynchronous

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

setTimeout(() => {
  console.log('3s');
}, 3000);
while(1);

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

已更新

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

最佳答案

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


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

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

基于事件的计时器

一个天真的实现是使用 MessageEvent接口(interface)和轮询,直到时间已到。但这对于长时间超时来说并不是真正的建议,因为这会迫使事件循环不断轮询新任务,这对树不利。

function myTimer(cb, ms) {
  const begin = performance.now();
  const channel = myTimer.channel ??= new MessageChannel();
  const controller = new AbortController();
  channel.port1.addEventListener("message", (evt) => {
    if(performance.now() - begin >= ms) {
      controller.abort();
      cb();
    }
    else if(evt.data === begin) channel.port2.postMessage(begin);
  }, { signal: controller.signal });
  channel.port1.start();
  channel.port2.postMessage(begin);
}

myTimer(() => console.log("world"), 2000);
myTimer(() => console.log("hello"), 100);

因此,如果可用的话,人们可能想使用 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);

不同线程上的无限循环

是的,使用 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);
<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>

对于那些喜欢炫耀他们知道尚未真正可用的最新功能的人(完全不是我的风格),稍微提一下即将到来的 Prioritized Post Task API 及其 delayed tasks ,这基本上是一个更强大的 setTimeout,返回一个 promise ,我们可以在上面设置优先级。

(async () => {
  if(globalThis.scheduler) {
    const p1 = scheduler.postTask(()=>{ console.log("world"); }, { delay: 2000} );
    const p2 = scheduler.postTask(()=>{ console.log("hello"); }, { delay: 1000} );
    await p2;
    console.log("future");
  }
  else {
    console.log("Your browser doesn't support this API yet");
  }
})();

关于javascript - 如何创建自己的 setTimeout 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50501356/

相关文章:

javascript - 如何延迟执行JavaScript中的特定函数?

javascript - 将新 CSS 类应用于遗留数据的最佳实践

javascript - 如果默认情况下 JavaScript 既不是异步的也不是多线程的,为什么新手通常认为它是(尤其是与其他语言相比)?

asynchronous - Blob.UploadAsync 方法上的 StorageClientException

javascript - 先根据HTML选择显示列表,点击HTML按钮后根据选择定向到特定页面

javascript - 在 jsTree 中,使节点文本可点击的最佳方法是什么(用于选中复选框)

javascript - 如何嵌入独立的 SVG 代码?

iOS/Swift - 使用 AWS3 (Amazon SDK) 将图像异步下载到 Collection View 中

javascript - ES6 生成器 : transforming callbacks to iterators

python - Python RQ 的作业推送性能不理想