如何创建自己的setTimeout函数?

时间:2018-05-24 05:04:45

标签: javascript asynchronous

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

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

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

更新

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

5 个答案:

答案 0 :(得分:4)

没有调用setTimeout()回调的原因是,代码中有while(1),它充当无限循环。它将使你的javascript堆栈保持整个时间,这就是事件循环永远不会在堆栈中推送setTimeout()的回调函数的原因。

如果从代码中删除while(1),则应调用setTimeout()的回调。

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

答案 1 :(得分:3)

因为我真的不知道为什么你不能使用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);




相反,如果可用,可能需要使用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>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

您好,可以尝试一下。 ] 它会有所帮助。谢谢

function customSetTimeOut (callback, ms) {
        var dt = new Date();
        var i = dt.getTime();
        var future = i + ms;
        while(Date.now() <= future) {
            //do nothing - blocking
        }
        return callback();
}

customSetTimeOut(function(){
  console.log("Timeout success");
},1000);

答案 3 :(得分:0)

要创建自己的setTimeout函数,可以使用以下函数setMyTimeout()来执行此操作而不使用setTimeout。

var foo= ()=>{
  console.log(3,"Called after 3 seconds",new Date().getTime());
}
var setMyTimeOut = (foo,timeOut)=>{
	let timer;
  let currentTime = new Date().getTime();
  let blah=()=>{

      if (new Date().getTime() >= currentTime + timeOut) {
        clearInterval(timer);
        foo()
      }
  }
  timer= setInterval(blah, 100);
}
console.log(1,new Date().getTime());
setMyTimeOut(foo,3000)
console.log(2,new Date().getTime());

答案 4 :(得分:0)

以下是自定义setTimeout和setInterval,clearTimeout和clearInterval的实现。我创建了它们以在内置setTimeout和setInterval无法使用的沙盒环境中使用。

const setTimeouts = [];
export function customSetTimeout(cb, interval) {
  const now = window.performance.now();
  const index = setTimeouts.length;
  setTimeouts[index] = () => {
    cb();
  };
  setTimeouts[index].active = true;
  const handleMessage = (evt) => {
    if (evt.data === index) {
      if (window.performance.now() - now >= interval) {
        window.removeEventListener('message', handleMessage);
        if (setTimeouts[index].active) {
          setTimeouts[index]();
        }
      } else {
        window.postMessage(index, '*');
      }
    }
  };
  window.addEventListener('message', handleMessage);
  window.postMessage(index, '*');
  return index;
}

export function customClearTimeout(setTimeoutId) {
  if (setTimeouts[setTimeoutId]) {
    setTimeouts[setTimeoutId].active = false;
  }
}

const setIntervals = [];
export function customSetInterval(cb, interval) {
  const intervalId = setIntervals.length;
  setIntervals[intervalId] = function () {
    if (setIntervals[intervalId].active) {
      cb();
      customSetTimeout(setIntervals[intervalId], interval);
    }
  };
  setIntervals[intervalId].active = true;
  customSetTimeout(setIntervals[intervalId], interval);
  return intervalId;
}

export function customClearInterval(intervalId) {
  if (setIntervals[intervalId]) {
    setIntervals[intervalId].active = false;
  }
}