真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

最近筆者在開發(fā)的過程中,因?yàn)樾枰獙撁孢M(jìn)行動(dòng)態(tài)渲染,又不想一直刷新頁面數(shù)據(jù),而是讓瀏覽器根據(jù)客戶需要?jiǎng)討B(tài)對渲染數(shù)據(jù),這個(gè)時(shí)候就用到了setInterval這個(gè)函數(shù),可是這個(gè)函數(shù)和setTimeout()相比又有什么區(qū)別呢?我們知道:

網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)的開發(fā),更需要了解用戶,從用戶角度來建設(shè)網(wǎng)站,獲得較好的用戶體驗(yàn)。創(chuàng)新互聯(lián)建站多年互聯(lián)網(wǎng)經(jīng)驗(yàn),見的多,溝通容易、能幫助客戶提出的運(yùn)營建議。作為成都一家網(wǎng)絡(luò)公司,打造的就是網(wǎng)站建設(shè)產(chǎn)品直銷的概念。選擇創(chuàng)新互聯(lián)建站,不只是建站,我們把建站作為產(chǎn)品,不斷的更新、完善,讓每位來訪用戶感受到浩方產(chǎn)品的價(jià)值服務(wù)。

  • setTimeout()是綁定在瀏覽器window的一個(gè)方法,可以通過這個(gè)方法指定一段代碼或者函數(shù)在多少毫秒(ms)后執(zhí)行,并返回該定時(shí)器的編號,當(dāng)然了我們可以通過clearTimeout()取消代碼的執(zhí)行
  • setInterval()綁定在瀏覽器window的一個(gè)方法,可以通過setInterval指定一段代碼或函數(shù)定時(shí)在多少毫秒(ms)后執(zhí)行,并傳回該定時(shí)器的編號,當(dāng)然了我們可以通過clearInterval()取消代碼的執(zhí)行
    最有效且直接的就是做個(gè)小實(shí)驗(yàn)感受下:

setInterval()執(zhí)行方法其實(shí)是將需執(zhí)行的代碼加入到任務(wù)隊(duì)列,直到輪到代碼執(zhí)行時(shí),確定時(shí)間是否已經(jīng)到了,若到達(dá)則執(zhí)行代碼

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

上面這段代碼的執(zhí)行之后,你會(huì)發(fā)現(xiàn)setInterval的end與start時(shí)間跳動(dòng)非常大,并不是我們設(shè)置的1000ms。由于setInterval是一開始就標(biāo)定了執(zhí)行的時(shí)間點(diǎn),當(dāng)所注冊的函數(shù)(func)超過,因此不會(huì)是固定的1000ms。
setTimeout() 與 setInterval() 的源碼分析
setTimeout()的用法大致與setInterval相同,不同的在于定時(shí)執(zhí)行,我們同樣來測試延遲執(zhí)行的現(xiàn)象

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

觀察下圖可以發(fā)現(xiàn) setTimeout 所設(shè)置的時(shí)間間隔,會(huì)因?yàn)槟壳叭蝿?wù)隊(duì)列所執(zhí)行的代碼而可能發(fā)生延誤執(zhí)行的情況,我們可以發(fā)現(xiàn)上面這段代碼,執(zhí)行func的end與start時(shí)間間隔基本上是符合我們所設(shè)定的1000ms
setTimeout() 與 setInterval() 的源碼分析
透過現(xiàn)象看本質(zhì),我們不妨看下這兩個(gè)函數(shù)的源碼
setTimeout() 與 setInterval() 的源碼分析
從函數(shù)聲明可以知道setInterval這里允許傳參,允許我們傳入一個(gè)方法,讓其在一定時(shí)間(number)后執(zhí)行,其時(shí)間等待依賴于調(diào)度器_scheduler,而方法的執(zhí)行是由_fnAndFlush來負(fù)責(zé)調(diào)控的,不過由于有_requeuePeriodicTimer這個(gè)方法的存在使得時(shí)間間隔不是我們所設(shè)固定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的代碼,這個(gè)函數(shù)其實(shí)是將我們的所需要執(zhí)行的函數(shù)刷入內(nèi)存中,等待瀏覽器的執(zhí)行

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這個(gè)方法所完成的功能的特點(diǎn)。其會(huì)將瀏覽器內(nèi)存中存在的函數(shù)加入隊(duì)列中執(zhí)行(如果存在的話),函數(shù)功能完成過程中已經(jīng)進(jìn)入下個(gè)scheduler的執(zhí)行周期,即函數(shù)在執(zhí)行過程中還有一個(gè)計(jì)時(shí)器在等待下個(gè)函數(shù)的執(zhí)行。正如實(shí)驗(yàn)中,我們觀察到的所有的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這里允許傳參,允許我們傳入一個(gè)方法,讓其在一定時(shí)間(number)后執(zhí)行,其時(shí)間等待依賴于調(diào)度器_scheduler,而調(diào)度器正是讓我們的函數(shù)時(shí)間間隔符合我們設(shè)置的1000ms的原因,而方法的執(zhí)行則是由_fnAndFlush來負(fù)責(zé)調(diào)控的,因?yàn)閟etTimeout并不會(huì)將執(zhí)行函數(shù)推入隊(duì)列中,因此計(jì)時(shí)器不會(huì)運(yùn)行直到前一個(gè)周期的函數(shù)都執(zhí)行完畢之后才開始進(jìn)入下一個(gè)周期,正如實(shí)驗(yàn)代碼中所寫的等待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/


文章標(biāo)題:setTimeout()與setInterval()的源碼分析
分享URL:http://weahome.cn/article/jhpocp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部