Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 133 题:用 setTimeout 实现 setInterval,阐述实现的效果与setInterval的差异

Open impeiran opened this issue 4 years ago • 36 comments

当中应该涉及setInterval的特性,node环境与浏览器的又会作何表现

impeiran avatar Aug 28 '19 05:08 impeiran

function timeout(fn,time){ var timer; if(timer){ clearTimeout(timer) } return function() { timer = setTimeout(function(){ fn(); timeout(fn,time)() },time); } } function interval (fn,time) { timeout(fn,time)() }

neofanfei avatar Aug 28 '19 09:08 neofanfei

function mySetInterval() {
  var args = arguments
  var timer = setTimeout(() => {
    args[0]()
    args.callee(...args)
  }, args[1])
  return timer
}

var timer = mySetInterval(() => {
  console.log(111)
}, 1000)

// clearInterval 清除计时器的方法还不知道怎么实现

受下面那位老哥@weiweixuan的启发 ,改了下代码,实现了清理功能

    function mySetInterval() {
        mySetInterval.timer = setTimeout(() => {
            arguments[0]()
            mySetInterval(...arguments)
        }, arguments[1])
    }

    mySetInterval.clear = function() {
        clearTimeout(mySetInterval.timer)
    }

    mySetInterval(() => {
        console.log(11111)
    }, 1000)

    setTimeout(() => {
        // 5s 后清理
        mySetInterval.clear()
    }, 5000)

hugeorange avatar Aug 29 '19 02:08 hugeorange

   const mySetInterval = (fn,duration)=>{
        const timer = {}
        function timeout(fn,duration){
            return setTimeout(()=>{
                fn();
                timer.timeout = timeout(fn,duration)
            },duration)
        }
        timer.timeout = timeout(fn,duration)
        return timer
    }
    const clearMySetInterval = (timer)=>{
        clearTimeout(timer.timeout)
    }
    //test
    console.time('timer')
    console.time('interval')
    const timer = mySetInterval(()=>console.timeLog('timer'),300)
    const interval = setInterval(()=>console.timeLog('interval'),300)
    setTimeout(()=>{
        clearMySetInterval(timer)
        clearInterval(interval)
    },3000)

自己实现的的setTimerout回调版只能打印9次,会越来越慢。原版setInterval能打印10次

shadowdreamer avatar Aug 29 '19 03:08 shadowdreamer

    var timer = null;
    var a = function(t){
        console.log(new Date().valueOf())
        timer= setTimeout(a,t)

    }
    a.close=function(){
        clearTimeout(timer)
    }
    a(1000) // 模拟一秒一次
   a.close() // 关闭

// 区别

IAMSBLOL avatar Aug 29 '19 03:08 IAMSBLOL

code2

weiweixuan avatar Aug 29 '19 04:08 weiweixuan

带清理功能的

code4

weiweixuan avatar Aug 29 '19 04:08 weiweixuan

  1. 简单的代码实现
    function foo(){
        console.log("执行foo");
        setTimeout(foo, 1000)
    }
    foo();
    function goo(){
        console.log("执行goo");
    }
    setInterval(goo, 1000);
  1. 之前学习setInterval,设置的间隔时间过短的时候,如果代码块里的代码并没有执行完也会重新开始执行?(我一直都是这么理解的),但使用setTimeout实现setInterval的这个效果就没这个问题,必然会把代码块中的代码运行玩后,然后才会再次调用该函数
  2. requestAnimationFrame的兼容写法应该就是类似我这个setTimeout实现setInterval的代码
  3. 求大佬指正轻喷,本人萌新- -

guqianfeng avatar Aug 29 '19 05:08 guqianfeng

  1. 简单的代码实现
    function foo(){
        console.log("执行foo");
        setTimeout(foo, 1000)
    }
    foo();
    function goo(){
        console.log("执行goo");
    }
    setInterval(goo, 1000);
  1. 之前学习setInterval,设置的间隔时间过短的时候,如果代码块里的代码并没有执行完也会重新开始执行?(我一直都是这么理解的),但使用setTimeout实现setInterval的这个效果就没这个问题,必然会把代码块中的代码运行玩后,然后才会再次调用该函数
  2. requestAnimationFrame的兼容写法应该就是类似我这个setTimeout实现setInterval的代码
  3. 求大佬指正轻喷,本人萌新- -

只有你提到了setInterval的那个痛点..

impeiran avatar Aug 29 '19 07:08 impeiran

大概实现了一下?返回值和原生略有不同使用了对象方便访问:

function myInterval(func, duration) {
  let tag = {
    flag: +new Date
  }
  const f = () => setTimeout(() => {
    if(tag.flag){
      func()
      f()
    }
  }, duration)
  f()
  return tag
}

function myClear(tag) {
  tag.flag = 0
}

let t = myInterval(() => console.log(1), 3000)

myClear(t)

facingcheer avatar Aug 29 '19 07:08 facingcheer

timerFun(); function timerFun() { console.log('1'); var timer = setTimeout(function() { timerFun(); clearTimeout(timer) }, 1000) }

JaniceDong avatar Aug 29 '19 09:08 JaniceDong

@hugeorange 这种实现与setInterval的差异呢?

qiujuyingshuang avatar Aug 29 '19 12:08 qiujuyingshuang

function _setinterval(callback,time){
  let timer = {}
  function run(){
    clearTimeout(timer)
    timer = setTimeout(()=>{
      callback()
      run()
    },time)
  }
  run()
  return {
    clear(){
      clearTimeout(timer)
    }
  }
}

function _clearInterval(timer){
  timer.clear()
}

let callback = ()=>{
  console.count()
}

let timer = _setinterval(callback,2000)

setTimeout(function(){
  _clearInterval(timer)
},2000*10)

ddchef avatar Aug 30 '19 06:08 ddchef

function mySetInterval() { mySetInterval.id = setTimeout(() => { arguments0 mySetInterval(...arguments) }, arguments[1]) }

mySetInterval.clearInterval = function (intervalId) { clearTimeout(intervalId) }

mySetInterval( () => { console.log('1') }, 1000)

setTimeout(() => { mySetInterval.clearInterval(mySetInterval.id) }, 5000)

734776328 avatar Aug 31 '19 13:08 734776328

预备知识

在浏览器中,setInterval的方法定义为:

long setInterval(in any handler, in optional any timeout, in any... args);

可以看出,该方法返回的句柄是不变的 long 值,我们需要通过该句柄去取消定时器

另一个要注意的点是:该方法的执行上下文必须为 window,WorkerUtils ,或者 实现 WindowTimers interface的对象(这个目前不知道怎么实现)

interface WindowTimers {
  long setTimeout(in any handler, in optional any timeout, in any... args);
  void clearTimeout(in long handle);
  long setInterval(in any handler, in optional any timeout, in any... args);
  void clearInterval(in long handle);
};
Window implements WindowTimers;

注意clearInterval(lone handler) 会对 handler 做隐式类型转换,下文有用到该特性

而在node环境中, setInterval 返回的是一个 Timeout 对象,


clearInterval(object timer), 故 clearInterval 不会对其中的参数做隐式类型转换(https://github.com/nodejs/node/blob/master/lib/timers.js#L194)

setTimeout 模拟

setTimeout 模拟 setInterval(handler,?timeout,...args) ,有两种实现:

注意这里我们要返回一个 timer的引用,但是timer又只能是Int,只能采取重写 valueOf 的方式实现

  1. 先执行 fn 再 重新设置 setTimeout
function setInterval1 (handler,timeout,...args) {
  let isBrowser = typeof window !=='undefined'
  if(isBrowser && this!==window){
    throw 'TypeError: Illegal invocation'
  }
  let timer = {}
  if(isBrowser){
    // 浏览器上处理
    timer = {
      value:-1,
      valueOf: function (){
        return this.value
      }
    }
    let callback = ()=>{
      handler.apply(this,args)
      timer.value = setTimeout(callback,timeout)
    }
    timer.value = setTimeout(callback,timeout)
  } else {
    // nodejs的处理
    let callback = ()=>{
      handler.apply(this,args)
      Object.assign(timer,setTimeout(callback,timeout))
    }
    Object.assign(timer,setTimeout(callback,timeout))
  }
  return timer
}

测试用例:

// 基础功能:不断的打印3
setInterval1 ((a,b)=>console.log(a+b),1000,1,2) 
// 清除定时器: 打印10次3后停止
let t = setInterval1 ((a,b)=>console.log(a+b),1000,1,2)
setTimeout(()=>{
  window.clearInterval(t)
},10.5*1000)
// this:前两个均提示非法调用错误,最后一个可以成功调用 定时输出 undefined
// node 下都可以调用
let tmp = {
  a:1,
  test:setInterval
}
let tmp1 = {
  a:1,
  test:setInterval1
}
tmp.test(function(){
  console.log(this.a)
},1000)
tmp1.test(function(){
  console.log(this.a)
},1000)
tmp1.test.call(window,() =>{
  console.log(this.a)
},1000)
  1. 先设置 setTimeout 再执行 fn
function setInterval2 (handler,timeout,...args) {
  let isBrowser = typeof window !=='undefined'
  if(isBrowser && this!==window){
    throw 'TypeError: Illegal invocation'
  }
  let timer = {}
  if(isBrowser){
    // 浏览器上处理
    timer = {
      value:-1,
      valueOf: function (){
        return this.value
      }
    }
    let callback = ()=>{
      // 区别在这
      timer.value = setTimeout(callback,timeout)
      handler.apply(this,args)
    }
    timer.value = setTimeout(callback,timeout)
  } else {
    // nodejs的处理
    let callback = ()=>{
      // 区别在这
      Object.assign(timer,setTimeout(callback,timeout))
      handler.apply(this,args)
    }
    Object.assign(timer,setTimeout(callback,timeout))
  }
  return timer
}

setInterval 、 setInterval1 、 setInterva2 三者差异对比

先编写预处理函数

function setInterval1 (handler,timeout,...args) {
  let isBrowser = typeof window !=='undefined'
  if(isBrowser && this!==window){
    throw 'TypeError: Illegal invocation'
  }
  let timer = {}
  if(isBrowser){
    // 浏览器上处理
    timer = {
      value:-1,
      valueOf: function (){
        return this.value
      }
    }
    let callback = ()=>{
      handler.apply(this,args)
      timer.value = setTimeout(callback,timeout)
    }
    timer.value = setTimeout(callback,timeout)
  } else {
    // nodejs的处理
    let callback = ()=>{
      handler.apply(this,args)
      Object.assign(timer,setTimeout(callback,timeout))
    }
    Object.assign(timer,setTimeout(callback,timeout))
  }
  return timer
}
function setInterval2 (handler,timeout,...args) {
  let isBrowser = typeof window !=='undefined'
  if(isBrowser && this!==window){
    throw 'TypeError: Illegal invocation'
  }
  let timer = {}
  if(isBrowser){
    // 浏览器上处理
    timer = {
      value:-1,
      valueOf: function (){
        return this.value
      }
    }
    let callback = ()=>{
      // 区别在这
      timer.value = setTimeout(callback,timeout)
      handler.apply(this,args)
    }
    timer.value = setTimeout(callback,timeout)
  } else {
    // nodejs的处理
    let callback = ()=>{
      // 区别在这
      Object.assign(timer,setTimeout(callback,timeout))
      handler.apply(this,args)
    }
    Object.assign(timer,setTimeout(callback,timeout))
  }
  return timer
}
// 同步处理函数
function syncHandler(ms) {
  let d = Date.now()
  while (Date.now() - d < ms) { }
}
// 异步处理函数
function asyncHandler(callback,ms){
  setTimeout(callback,ms)
}
let scope = typeof window !=='undefined'?window:global
// 主测试函数
function test(setInterval,count){
  return (handler,timeout,...args) => {
    let t = setInterval (handler,timeout,...args)
    setTimeout(()=>{
      scope.clearInterval(t)
    },(count+0.5)*timeout)
  }
}

1. handler 为同步处理函数

  • setInterval
var start = Date.now()
var icounter = 0
test(setInterval,5)(function(){
  var time = (Date.now() - start) / 1000
  console.log('setInterval=>次数:' + (++icounter) + '    所用时间:' + time.toFixed(3))
  syncHandler(100)
},1000)
/*
# chrome76
setInterval=>次数:1    所用时间:1.002
setInterval=>次数:2    所用时间:2.001
setInterval=>次数:3    所用时间:3.000
setInterval=>次数:4    所用时间:4.002
setInterval=>次数:5    所用时间:5.002
# node v10
setInterval=>次数:1    所用时间:1.003
setInterval=>次数:2    所用时间:2.004
setInterval=>次数:3    所用时间:3.005
setInterval=>次数:4    所用时间:4.005
setInterval=>次数:5    所用时间:5.005
*/
  • setInterval1
var start = Date.now()
var icounter = 0
test(setInterval1,5)(function(){
  var time = (Date.now() - start) / 1000
  console.log('setInterval1=>次数:' + (++icounter) + '    所用时间:' + time.toFixed(3))
  syncHandler(100)
},1000)
/*
# chrome76
setInterval1=>次数:1    所用时间:1.001
setInterval1=>次数:2    所用时间:2.103
setInterval1=>次数:3    所用时间:3.204
setInterval1=>次数:4    所用时间:4.305
setInterval1=>次数:5    所用时间:5.406
# node v10
setInterval1=>次数:1    所用时间:1.005
setInterval1=>次数:2    所用时间:2.121
setInterval1=>次数:3    所用时间:3.224
setInterval1=>次数:4    所用时间:4.324
setInterval1=>次数:5    所用时间:5.428
*/
  • setInterval2
var start = Date.now()
var icounter = 0
test(setInterval2,5)(function(){
  var time = (Date.now() - start) / 1000
  console.log('setInterval2=>次数:' + (++icounter) + '    所用时间:' + time.toFixed(3))
  syncHandler(100)
},1000)
/*
# chrome76
setInterval2=>次数:1    所用时间:1.001
setInterval2=>次数:2    所用时间:2.004
setInterval2=>次数:3    所用时间:3.005
setInterval2=>次数:4    所用时间:4.007
setInterval2=>次数:5    所用时间:5.008
# node v10
setInterval2=>次数:1    所用时间:1.006
setInterval2=>次数:2    所用时间:2.005
setInterval2=>次数:3    所用时间:3.005
setInterval2=>次数:4    所用时间:4.005
setInterval2=>次数:5    所用时间:5.005
*/

当 handler 为同步处理函数且执行时间小于 timeout,我们可以得到以下结论:

  1. 浏览器执行结果与 nodejs 没有差异
  2. setInterval 与 setInterval2 效果相近,说明 setInterval 是先将自身 handle 放入timer堆,再执行回调函数
  3. 先执行回调函数再设置 settimeout 会导致下次执行实现等待时间大于 timeout+同步代码执行时间

通过 node-libuv 源码可以证明

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min(timer_heap(loop));//取出timer堆上超时时间最小的元素
    if (heap_node == NULL)
      break;
    //根据上面的元素,计算出handle的地址,head_node结构体和container_of的结合非常巧妙,值得学习
    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)//如果最小的超时时间比循环运行的时间还要小,则表示没有到期的callback需要执行,此时退出timer阶段
      break;

    uv_timer_stop(handle);//将这个handle移除
    uv_timer_again(handle);//如果handle是repeat类型的,重新插入堆里
    handle->timer_cb(handle);//执行handle上的callback
  }
}

francecil avatar Sep 01 '19 08:09 francecil

  setTimeout(dunction(){
    //处理代码
    setTimeout(arguments.callee,ms)
  },ms)

Yxiuchao avatar Sep 01 '19 12:09 Yxiuchao

setInterval

  1. 标准中,setInterval()如果前一次代码没有执行完,则会跳过此次代码的执行。
  2. 浏览器中,setInterval()如果前一次代码没有执行完,不会跳过此次代码,而是将其插在队列中,等待前一次代码执行完后立即执行。
  3. Node中,setInterval()会严格按照间隔时间执行:一直等待完成上一次代码函数后,再经过时间间隔,才会进行下一次调用。

有测试过或者能提供相关文献么? 我这里测试 node v10和 chrome 76效果是一样的,即等待前一次代码执行完后立即执行

francecil avatar Sep 02 '19 11:09 francecil

function mySetInterval(fn,time){ function inner(){ fn(); setTimeout(inner,time); } inner() }

mySetInterval(() => {console.log("sss")}, 1000)

DaYesahh avatar Sep 02 '19 11:09 DaYesahh

@francecil 我在浏览器 里试验的是 setInterval()如果前一次代码没有执行完,不会跳过此次代码,等待前一次代码执行完后执行。(是不是立即,还有待测试

        function customerTimeInterval(index) {
            for (let i = 0; i < 1000; i ++) {
                console.log(index);
            }
        }
        let i = 0;
        this.timer = setInterval(() => {
            i ++;
            if (i === 5) {
                this.timer && clearTimeout(this.timer);
            }
            customerTimeInterval(i);
        }, 1);

结果: result

GodVampire avatar Sep 03 '19 02:09 GodVampire

setInterval

  1. 标准中,setInterval()如果前一次代码没有执行完,则会跳过此次代码的执行。
  2. 浏览器中,setInterval()如果前一次代码没有执行完,不会跳过此次代码,而是将其插在队列中,等待前一次代码执行完后立即执行。
  3. Node中,setInterval()会严格按照间隔时间执行:一直等待完成上一次代码函数后,再经过时间间隔,才会进行下一次调用。

有测试过或者能提供相关文献么? 我这里测试 node v10和 chrome 76效果是一样的,即等待前一次代码执行完后立即执行 @francecil 我经测试后结果也跟你一样,抱歉没注意那篇文献的日期,大意了,已删除issue comment

impeiran avatar Sep 03 '19 03:09 impeiran

 setTimeout(function(){
            //todo
            setTimeout(arguments.callee,time)
        },time)

276378532 avatar Sep 07 '19 02:09 276378532

let n=0
let id = setInterval(() => {
    n+=1
    console.log(n)
    if ( n>=10 ){
        window.clearInterval(id)
    }
})
  • setTimeout模拟
let n=0
let id = setTimeout(function fn(){
    n+=1
    console.log(n)
    if(n<10){
       setTimeout(fn,500)
    }
},500)

fyZhang66 avatar Sep 15 '19 08:09 fyZhang66

function mySetInterval() {
    let timeout;
    let args = arguments;

    (function run() {
        let self = this
        timeout = setTimeout(() => {
            args[0]();
            run.apply(self,[...args]);
        },args[1])
    })()
    return {
        clearInterval: function() {
            clearTimeout(timeout)
        }
    }
}
function clearMyInterval(time){
    time.clearInterval()
}
let test = mySetInterval(() => {
    console.log('111')
},2000)

setTimeout(() => {
    clearMyInterval(test)
},5000)

yuwanli avatar Sep 28 '19 14:09 yuwanli

// 自启动,自关闭
function mySetInterval(intervalSign, cb, delay) {
    intervalSign ? mySetInterval.timer = setTimeout(() => {
        typeof cb === 'function' && cb();
        mySetInterval(intervalSign, cb, delay)
    }, delay) : clearTimeout(mySetInterval.timer);
    
}
// 传参为true开启定时器
mySetInterval(true, () => {
    console.log('setInterval log')
}, 1000);
// 传参为false,关闭定时器
mySetInterval(false);

我感觉这样的模拟实现会让定时器误差变大,每次调用setTimeout开启定时器也有一定的时间消耗. 看issue别的大佬的回复, 设置的间隔时间过短的时候,如果代码块里的代码并没有执行完也会重新开始执行,但使用setTimeout实现setInterval的这个效果就没这个问题,必然会把代码块中的代码运行完后,然后才会再次调用该函数.

muzishuiji avatar Oct 28 '19 06:10 muzishuiji

function mySetInterval(fn, time) {
	let timer = null;
	// 定义内部函数
	function interval() {
		clearTimeout(timer)
		timer = setTimeout(()=> {
			fn()
			interval()   // fn执行完后再次执行interval
		}, time)
	}
	interval()
	// 取消函数
	interval.cancel = function() {
		clearTimeout(timer)
	}
	return interval;
}

// 使用方法
let timer = mySetInterval(() => {
	   console.log(11111)
}, 1000)

setTimeout(() => {
    // 5s 后清理
   timer.cancel()
 }, 5000)

lpdong avatar Nov 20 '19 01:11 lpdong

!function timer() {
    return !function () {
        var timeid = setTimeout(function () {
            console.log("hahaha...")
            clearTimeout(timeid)
            timer()
        }, 1000)
    }()
}()

hahaha...(6个时候) [Done] exited with code=1 in 7.024 seconds

hahaha...(19个时候) [Done] exited with code=1 in 19.326 seconds

(不过这个测试不够准确.......)

lanOrage avatar Dec 07 '19 08:12 lanOrage

      function setIntervalBySetTimeout (fn, timeout) {
        function initTimeout () {
          clearTimeout(fn._tid);
          fn._tid = setTimeout(() => {
            fn();
            initTimeout();
          }, timeout);
        }

        initTimeout();
      }

      const callback = () => {
        console.log(11111);
      };
      // 开始定时器
      setIntervalBySetTimeout(callback, 1000);

      // 5秒后关闭定时器
      setTimeout(() => {
        clearTimeout(callback._tid);
      }, 5000);

soneway avatar Dec 29 '19 03:12 soneway

hugeorange 的写法有问题,函数应该允许反复调用,如果在函数上定义timer变量,当我多次调用方法时,timer永远是最后一次的,这会导致之前的定时器不能正确使用clear方法。

我的改写:

function simuInterval(fn, mills) {
  let timer = null;
  (function loop() {
    timer = setTimeout(() => {
      loop();
      fn();
    }, mills);
  })();

  return () => {
    clearTimeout(timer);
  };
}

另外很多人写的有点问题,如果我们在fn里面执行clear计时器操作,那么必须将重启timer操作前置,不然将导致clear失败;比如上面代码如果写成:

fn();
loop();

当我在fn内做了clear,但是loop紧接着会重启,这导致clear操作失败。

测试用例:

let clear = simuInterval(() => {
  console.log('should only run one time');
  clear();
}, 100);

whosesmile avatar Mar 18 '20 04:03 whosesmile

function mySetInterval(fn, ms) {
    return {
        start_id: null,
        start: function() {
            var that = this;
            // that.clear();
            that.start_id = setTimeout(function() {
                fn();
                that.start();
            }, ms);
        },
        clear: function() {
            console.log('timer clear');
            var that = this;
            setTimeout(function() { // 解决再fn内部取消定时会失效的情况
                clearTimeout(that.start_id);
            }, 0)
        }
    };
}
var count = 0;
var t = mySetInterval(function() {
    console.log(count++);
    if (count > 10) {
        console.log('clear');
        t.clear();
    }
    // t.clear();
}, 100);

t.start();

codedaybyday avatar Aug 16 '20 16:08 codedaybyday

模拟的setInterval 和原生setInterval 没有差异?


function _setInterval(fn, interval) {
    var timer = {}
    var timerID = 0
    if (typeof window !== "undefined") {
        timer = {
            valueOf() {
                return timerID
            }
        }
    }

    var oneTime = function () {
        if (typeof window !== "undefined") {
            timerID = setTimeout(() => {
                oneTime()
                fn()
            }, interval)
        } else {
            Object.assign(timer, setTimeout(() => {
                oneTime()
                fn()
            }, interval))
        }
    }
    oneTime()
    return timer;
}
setTimeout(() => {
    console.log('插入耗时计算get(8000000)')
    get(8000000)
}, 2000);

var nu = 0
var timer = _setInterval(function () {
    console.log('interval', nu)
    var data = new Date();
    var str = data.getMinutes() + ":" + data.getSeconds() + ":" + data.getMilliseconds();
    console.log(str);
    if (nu > 3) {
        console.log('clearInterval')
        clearInterval(timer)
    }
    nu++
    // get(8000000)
}, 1000)

// 耗时计算
function get(n) {
    var count = 0
    for (var i = 0; i <= n; i++) {
        var temp = String(i).match(/1/g)
        if (temp) {
            count += temp.length
        }
    }
    return count
}

yangchaojie456 avatar Sep 01 '20 06:09 yangchaojie456

function mySetinterval(fn, delay = 300) {
	let timer = function () {
		setTimeout(() => {
			fn()
			timer()
		}, delay)
	}
	timer()
	return function stop() {
		timer = () => {}
	}
}

let stop = mySetinterval(() => {
	console.log(1)
}, 2000)

setTimeout(() => {
	stop()
}, 10 * 1000)

mesterLi avatar Sep 22 '20 06:09 mesterLi

function mySetInterval(cb, delay) {
  const timerRef = {};

  function genTimeout() {
    clearTimeout(timerRef.value);

    return setTimeout(() => {
      cb();
      timerRef.value = genTimeout();
    }, delay);
  }

  timerRef.value = genTimeout();

  return  timerRef;
}

function myClearInterval(timerRef) {
  clearTimeout(timerRef.value);
}

jaan1025 avatar Jan 11 '21 16:01 jaan1025

const myInterval = (cb, span) => {
  let isRun = true
  const func = async () => {
    while (isRun) {
      await new Promise(resolve => {
        setTimeout(() => {
          cb()
          resolve()
        }, span)
      })
    }
  }
  func()
  return () => {
    isRun = false
  }
}

const clearFunc = myInterval(() => {
  console.log('hello')
}, 2000)

setTimeout(() => {
  clearFunc()
}, 6000)

setInterval能够保证以固定频率向事件队列放入回调,setTimeout不能保证。两个都不能保证固定的回调执行频率,因为存在主线程阻塞的可能

halionn avatar Apr 01 '21 02:04 halionn

差异就是假设js执行线程阻塞的话会使自己模拟的间隔增加,而setInterval在页面卡顿时依然会从计时器线程中创建任务增加至任务队列中

function myInterval(cb, time) {
    let id = setTimeout(() => {
      cb();
      myInterval(cb, time);
    }, time);
    myInterval.cancel = () => {
      clearTimeout(id);
    }
  }

ShaneQin avatar Apr 06 '21 14:04 ShaneQin

function time() {
            let timer;
            timer =  setTimeout(() => {
                clearTimeout(timer)
                console.log(1);
                time()
            }, 1000);
        }
        time()

yaoocheng avatar Nov 17 '21 05:11 yaoocheng

function mySetInterval() {
    const [handler, duration] = arguments;

    mySetInterval.timer = setTimeout(() => {
        handler();
        arguments.callee(...arguments)
    }, duration);
}

mySetInterval.clearInterval = function() {
  clearTimeout(mySetInterval.timer)
}

TheFirstSunday avatar Apr 19 '22 11:04 TheFirstSunday