setTimeout和setInterval的返回值是啥类型的,它有啥意义

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了setTimeout和setInterval的返回值是啥类型的,它有啥意义相关的知识,希望对你有一定的参考价值。

参考技术A 这个很简单 直接console.log(typeof setTimeout(function(),10000)) 就可以得到 number,

他在意义在与我们可以用他返回的这个值与终止它 如 clearTimeout(80),clearInterval(89)
参考技术B javascript中setTimeout和setInterval的返回值类型和意义说明:
1、setTimeout :暂停指定的毫秒数后执行指定的代码,返回值是id标识,这个id的意义就是通过clearTimeout来清理暂停执行函数。
setTimeout函数的ID标识,每次调用setTimeout函数都会产生一个唯一的ID,可以通过clearTimeout函数(此函数的参数接收一个setTimeout返回的ID)暂停setTimeout函数还未执行的代码
2、setTimeout :按照指定的周期(以毫秒计)来调用函数或计算表达式。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

setTimeout() 与 setInterval() 的源码分析

最近笔者在开发的过程中,因为需要对页面进行动态渲染,又不想一直刷新页面数据,而是让浏览器根据客户需要动态对渲染数据,这个时候就用到了setInterval这个函数,可是这个函数和setTimeout()相比又有什么区别呢?我们知道:

  • setTimeout()是绑定在浏览器window的一个方法,可以通过这个方法指定一段代码或者函数在多少毫秒(ms)后执行,并返回该定时器的编号,当然了我们可以通过clearTimeout()取消代码的执行
  • setInterval()绑定在浏览器window的一个方法,可以通过setInterval指定一段代码或函数定时在多少毫秒(ms)后执行,并传回该定时器的编号,当然了我们可以通过clearInterval()取消代码的执行
    最有效且直接的就是做个小实验感受下:

setInterval()执行方法其实是将需执行的代码加入到任务队列,直到轮到代码执行时,确定时间是否已经到了,若到达则执行代码

var startTime=new Date();
var func = function()
console.log(‘start: ‘ + (new Date()-startTime));
for(var i=0; i<1000000000; i++)
console.log(‘end: ‘ + (new Date()-startTime));
;
setInterval(func,1000);

上面这段代码的执行之后,你会发现setInterval的end与start时间跳动非常大,并不是我们设置的1000ms。由于setInterval是一开始就标定了执行的时间点,当所注册的函数(func)超过,因此不会是固定的1000ms。
技术图片
setTimeout()的用法大致与setInterval相同,不同的在于定时执行,我们同样来测试延迟执行的现象

var startTime=new Date();
var func = function()
console.log(‘start: ‘ + (new Date()-startTime));
for(var i=0; i<1000000000; i++);
console.log(‘end: ‘ + (new Date()-startTime));
setTimeout(func,1000);
;
setTimeout(func,1000);

观察下图可以发现 setTimeout 所设置的时间间隔,会因为目前任务队列所执行的代码而可能发生延误执行的情况,我们可以发现上面这段代码,执行func的end与start时间间隔基本上是符合我们所设定的1000ms
技术图片
透过现象看本质,我们不妨看下这两个函数的源码
技术图片
从函数声明可以知道setInterval这里允许传参,允许我们传入一个方法,让其在一定时间(number)后执行,其时间等待依赖于调度器_scheduler,而方法的执行是由_fnAndFlush来负责调控的,不过由于有_requeuePeriodicTimer这个方法的存在使得时间间隔不是我们所设固定1000ms

....
case ‘setInterval‘:
  task.data![‘handleId‘] = this._setInterval(
      task.invoke, task.data![‘delay‘]!,
      Array.prototype.slice.call((task.data as any)[‘args‘], 2));
  break; 
....

private _setInterval(fn: Function, interval: number, args: any[]): number 
  let id = Scheduler.nextId;
  let completers = onSuccess: null as any, onError: this._dequeuePeriodicTimer(id);
  let cb = this._fnAndFlush(fn, completers);

  // Use the callback created above to requeue on success.
  completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id);

  // Queue the callback and dequeue the periodic timer only on error.
  this._scheduler.scheduleFunction(cb, interval, args, true);
  this.pendingPeriodicTimers.push(id);
  return id;
  

先来看下_fnAndFlush的代码,这个函数其实是将我们的所需要执行的函数刷入内存中,等待浏览器的执行

private _fnAndFlush(fn: Function, completers: onSuccess?: Function, onError?: Function):
    Function 
  return (...args: any[]): boolean => 
    fn.apply(global, args);

    if (this._lastError === null)   // Success
      if (completers.onSuccess != null) 
        completers.onSuccess.apply(global);
      
      // Flush microtasks only on success.
      this.flushMicrotasks();
     else   // Failure
      if (completers.onError != null) 
        completers.onError.apply(global);
      
    
    // Return true if there were no errors, false otherwise.
    return this._lastError === null;
  ;

我们不妨来看下_requeuePeriodicTimer这个方法所完成的功能的特点。其会将浏览器内存中存在的函数加入队列中执行(如果存在的话),函数功能完成过程中已经进入下个scheduler的执行周期,即函数在执行过程中还有一个计时器在等待下个函数的执行。正如实验中,我们观察到的所有的start间隔大致是1000ms

private _requeuePeriodicTimer(fn: Function, interval: number, args: any[], id: number): Function 
  return () => 
    // Requeue the timer callback if it‘s not been canceled.
    if (this.pendingPeriodicTimers.indexOf(id) !== -1) 
      this._scheduler.scheduleFunction(fn, interval, args, true, false, id);
    
  ;

对于setTimeout这里允许传参,允许我们传入一个方法,让其在一定时间(number)后执行,其时间等待依赖于调度器_scheduler,而调度器正是让我们的函数时间间隔符合我们设置的1000ms的原因,而方法的执行则是由_fnAndFlush来负责调控的,因为setTimeout并不会将执行函数推入队列中,因此计时器不会运行直到前一个周期的函数都执行完毕之后才开始进入下一个周期,正如实验代码中所写的等待1000ms

...
case ‘setTimeout‘:
  task.data![‘handleId‘] = this._setTimeout(
      task.invoke, task.data![‘delay‘]!,
      Array.prototype.slice.call((task.data as any)[‘args‘], 2));
  break;
...
private _setTimeout(fn: Function, delay: number, args: any[], isTimer = true): number 
  let removeTimerFn = this._dequeueTimer(Scheduler.nextId);
  // Queue the callback and dequeue the timer on success and error.
  let cb = this._fnAndFlush(fn, onSuccess: removeTimerFn, onError: removeTimerFn);
  let id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer);
  if (isTimer) 
    this.pendingTimers.push(id);
  
  return id;
   

参考资料:
https://www.jeffjade.com/2016/01/10/2016-01-10-javacript-setTimeout/
https://www.jeffjade.com/2016/01/10/2016-01-10-javaScript-setInterval/

以上是关于setTimeout和setInterval的返回值是啥类型的,它有啥意义的主要内容,如果未能解决你的问题,请参考以下文章

setTimeout和setInterval

第一节:setTimeout和setInterval定时器

setTimeout和setInterval的返回值是啥类型的,它有啥意义

js中setInterval和setTimeout区别和用法

有关定时器setTimeout()setInterval()详解

超时定时器setTimeout和间歇定时器setInterval