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

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

....
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/

原文:https://blog.51cto.com/yerikyu/2434534

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!