亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

setTimeout() 與 setInterval() 的源碼分析

發布時間:2020-06-05 14:31:24 來源:網絡 閱讀:733 作者:yerikyu 欄目:web開發

最近筆者在開發的過程中,因為需要對頁面進行動態渲染,又不想一直刷新頁面數據,而是讓瀏覽器根據客戶需要動態對渲染數據,這個時候就用到了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() 的源碼分析
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
setTimeout() 與 setInterval() 的源碼分析
透過現象看本質,我們不妨看下這兩個函數的源碼
setTimeout() 與 setInterval() 的源碼分析
從函數聲明可以知道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/

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

塘沽区| 香河县| 措美县| 古蔺县| 敦化市| 宣化县| 内丘县| 定襄县| 南康市| 安陆市| 滨海县| 平阳县| 英超| 达日县| 筠连县| 三门峡市| 安溪县| 镶黄旗| 大姚县| 星子县| 比如县| 佛教| 东至县| 邵阳市| 黔东| 新昌县| 宜城市| 剑河县| 孟村| 海城市| 庄河市| 法库县| 上犹县| 宜章县| 丹阳市| 万全县| 陈巴尔虎旗| 九龙城区| 囊谦县| 大邑县| 芜湖市|