最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create your own setTimeout function? - Stack Overflow

programmeradmin1浏览0评论

I understand how to use setTimeout function, but I can't find a way to create a function like it.
I have an example:

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

The result is setTimeout callback never call so I think it use the same thread like every js other functions. But when it's check the time reach or not? and how it can do that?

Updated

To avoid misunderstanding I update my question.
I can't find a way to create a async function with callback after specify time (without using setTimeout and don't block entire thread). This function setTimeout seen like a miracle to me. I want to understand how it work.

I understand how to use setTimeout function, but I can't find a way to create a function like it.
I have an example:

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

The result is setTimeout callback never call so I think it use the same thread like every js other functions. But when it's check the time reach or not? and how it can do that?

Updated

To avoid misunderstanding I update my question.
I can't find a way to create a async function with callback after specify time (without using setTimeout and don't block entire thread). This function setTimeout seen like a miracle to me. I want to understand how it work.

Share Improve this question edited May 24, 2018 at 6:00 Đinh Anh Huy asked May 24, 2018 at 5:04 Đinh Anh HuyĐinh Anh Huy 5456 silver badges19 bronze badges 4
  • 4 You can't emulate setTimeout without using timeout. – Teemu Commented May 24, 2018 at 5:06
  • @Teemu thank for your help, so this function is some kind of special function that we can't emulate it? – Đinh Anh Huy Commented May 24, 2018 at 5:09
  • Why do you have while(1) in there? – Raeesaa Commented May 24, 2018 at 5:13
  • 1 It's much like an event which uses the in-built event queue (or something similar), you'd need to emulate the queue, and doing that wouldn't be very efficient. – Teemu Commented May 24, 2018 at 5:13
Add a comment  | 

5 Answers 5

Reset to default 10

Just for the game since I really don't see why you couldn't use setTimeout...


To create a non-blocking timer, without using the setTimeout/setInterval methods, you have only two ways:

  • event based timer
  • run your infinite loop in a second thread

Event based timer

One naive implementation would be to use the MessageEvent interface and polling until the time has been reached. But that's not really advice-able for long timeouts as this would force the event-loop to constantly poll new tasks, which is bad for trees.

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

So instead, if available, one might want to use the Web Audio API and the AudioScheduledSourceNode, which makes great use of the high precision Audio Context's own clock:

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

Infinite loop on a different thread

Yes, using Web Workers we can run infinite loops without killing our web page:

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>

And for the ones who like to show off they know about the latest features not yet really available (totally not my style), a little mention of the incoming Prioritized Post Task API and its delayed tasks, which are basically a more powerful setTimeout, returning a promise, on which we can set prioritization.

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

The reason callback of setTimeout() is not being called is, you have while(1) in your code which acts as infinite loop. It will keep your javascript stack busy whole time and that is the reason event loop will never push callback function of setTimeout() in stack.

If you remove while(1) from your code, callback for setTimeout() should get invoked.

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

To create your own setTimeout function, you can use the following function, setMyTimeout() to do that without using 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());

Following is the implementation of custom setTimeout and setInterval, clearTimeout and clearInterval. I created them to use in sandbox environments where builtin setTimeout and setInterval doesn't work.

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

Hi you can try this. ] HOpe it will help. Thanks

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

发布评论

评论列表(0)

  1. 暂无评论