国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

setTimeout 或者 setInterval,關(guān)于 Javascript 計(jì)時(shí)器:你需要知道的

Warren / 1670人閱讀

摘要:所以,我們可以將理解為計(jì)時(shí)結(jié)束是執(zhí)行任務(wù)的必要條件,但是不是任務(wù)是否執(zhí)行的決定性因素。的意思是,必須超過毫秒后,才允許執(zhí)行。

先來回答一下下面這個問題:對于 setTimeout(function() { console.log("timeout") }, 1000) 這一行代碼,你從哪里可以找到 setTimeout 的源代碼(同樣的問題還會是你從哪里可以看到 setInterval 的源代碼)?

很多時(shí)候,可以我們腦子里面閃過的第一個答案肯定是 V8 引擎或者其它 VM們,但是要知道的一點(diǎn)是,所有我們所見過的 Javascript 計(jì)時(shí)函數(shù),都沒有出現(xiàn)在 ECMAScript 標(biāo)準(zhǔn)中,也沒有被任何 Javascript 引擎實(shí)現(xiàn),計(jì)時(shí)函數(shù),其實(shí)都是由瀏覽器(或者其它運(yùn)行時(shí),比如 Node.js)實(shí)現(xiàn)的,并且,在不同的運(yùn)行時(shí)下,其表現(xiàn)形式有可能都不一致

在瀏覽器中,主計(jì)時(shí)器函數(shù)是 Window 接口的一部分,這保證了包括如 setTimeoutsetInterval 等計(jì)時(shí)器函數(shù)以及其它函數(shù)和對象能被全局訪問,這才是你可以隨時(shí)隨地使用 setTimeout 的原因。同樣的,在 Node.js 中,setTimeoutglobal 對象的一部分,這拿得你也可以像在瀏覽器里面一樣,隨時(shí)隨地的使用它。

到現(xiàn)在可能會有一些人感覺這個問題其實(shí)并沒有實(shí)際的價(jià)值,但是作為一個 Javascript 開發(fā)者,如果不知道本質(zhì),那么就有可能不能完全的理解 V8 (或者其它VM)是到底是如何與瀏覽器或者 Node.js 相互作用的。

暫緩一個函數(shù)的執(zhí)行

計(jì)時(shí)器函數(shù)都是更高階的函數(shù),它們可以用于暫緩一個函數(shù)的執(zhí)行,或者讓一個函數(shù)重復(fù)執(zhí)行(由他們的第一個參數(shù)執(zhí)行需要執(zhí)行的函數(shù))。

下面這是一個暫緩執(zhí)行的示例:

setTimeout(() => {
  console.log("距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了")
}, 4 * 1000)

在上面的示例中, setTimeoutconsole.log 的執(zhí)行暫緩了 4 * 1000 毫秒,也就是 4 秒鐘, setTimeout 的第一個函數(shù),就是需要暫緩執(zhí)行的函數(shù),它是一個函數(shù)的引用,下面這個示例是我們更加常見到的寫法:

const fn = () => {
  console.log("距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了")
}

setTimeout(fn, 4 * 1000)
傳遞參數(shù)

如果被 setTimeout 暫緩的函數(shù)需要接收參數(shù),我們可以從第三個參數(shù)開始添加需要傳遞給被暫緩函數(shù)的參數(shù):

const fn = (name, gender) => {
  console.log(`I"m ${name}, I"m a ${gender}`)
}

setTimeout(fn, 4 * 1000, "Tao Pan", "male")

上面的 setTimeout 調(diào)用,其結(jié)果與下面這樣調(diào)用類似:

setTimeout(() => {
  fn("Tao Pan", "male")
}, 4 * 1000)

但是記住,只是結(jié)果類似,本質(zhì)上是不一樣的,我們可以用偽代碼來表示 setTimeout 的函數(shù)實(shí)現(xiàn):

const setTimeout = (fn, delay, ...args) => {
  wait(delay) // 這里表示等待 delay 指定的毫秒數(shù)
  fn(...args)
}
挑戰(zhàn)一下

編寫一個函數(shù):

當(dāng) delay 為 4 秒的時(shí)候,打印出:距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了

當(dāng) delay 為 8 秒的時(shí)候,打印出:距離函數(shù)的調(diào)用,已經(jīng)過去 8 秒了

當(dāng) delay 為 N 秒的時(shí)候,打印出:距離函數(shù)的調(diào)用,已經(jīng)過去 N 秒了

下面這個是我的一個實(shí)現(xiàn):

const delayLog = delay => {
  setTimeout(console.log, delay * 1000, `距離函數(shù)的調(diào)用,已經(jīng)過去 ${delay} 秒了`)
}

delayLog(4) // 輸出:距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了
delayLog(8) // 輸出:距離函數(shù)的調(diào)用,已經(jīng)過去 8 秒了

我們來理一下 delayLog(4) 的整個執(zhí)行過程:

delay = 4

setTimeout 執(zhí)行

4 * 1000 毫秒后, setTimeout 調(diào)用 console.log 方法

setTimeout 計(jì)算其第三個參數(shù) 距離函數(shù)的調(diào)用,已經(jīng)過去 ${delay} 秒了 得到 距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了

setTimeout 將計(jì)算得到的字符串當(dāng)作 console.log 的第一個參數(shù)

console.log("距離函數(shù)的調(diào)用,已經(jīng)過去 4 秒了") 執(zhí)行,輸出結(jié)果

規(guī)律性重復(fù)一個函數(shù)的執(zhí)行以及停止重復(fù)調(diào)用

如果我們現(xiàn)在要每 4 秒第印一次呢?這里面就有很多種實(shí)現(xiàn)方式了,假如我們還是使用 setTimeout 來實(shí)現(xiàn),我們可以這樣做:

const loopMessage = delay => {
  setTimeout(() => {
    console.log("這里是由 loopMessage 打印出來的消息")
    loopMessage(delay)
  }, delay * 1000)
}

loopMessage(1) // 此時(shí),每過 1 秒鐘,就會打印出一段消息:*這里是由 loopMessage 打印出來的消息*

但是這樣有一個問題,就是開始之后,我們就沒有辦法停止,怎么辦?可以稍稍改改實(shí)現(xiàn):

let loopMessageTimer

const loopMessage = delay => {
  loopMessageTimer = setTimeout(() => {
    console.log("這里是由 loopMessage 打印出來的消息")
    loopMessage(delay)
  }, delay * 1000)
}

loopMessage(1)

clearTimeout(loopMessageTimer) // 我們隨時(shí)都可以使用 `clearTimeout` 清除這個循環(huán)

但是這樣還是有問題的,如果 loopMessage 被調(diào)用多次,那么他們將共用一個 loopMessageTimer,清除一個,將清除所有,這是肯定不行的,所以,還得再改造一下:

const loopMessage = delay => {
  let timer
  
  const log = () => {
    timer = setTimeout(() => {
      console.log(`每 ${delay} 秒打印一次`)
      log()
    }, delay * 1000)
  }

  log()

  return () => clearTimeout(timer)
}

const clearLoopMessage = loopMessage(1)
const clearLoopMessage2 = loopMessage(1.5)

clearLoopMessage() // 我們在任何時(shí)候都可以取消任何一個重復(fù)調(diào)用,而不影響其它的

這…… 實(shí)現(xiàn)是實(shí)現(xiàn)了,但是其它有更好的解決辦法:

const timer = setInterval(console.log, 1000, "每 1 秒鐘打印一次")

clearInterval(timer) // 隨時(shí)可以 `clearInterval` 清除
更加深入了認(rèn)識取消計(jì)時(shí)器(Cancel Timers)

上面的示例只是簡單的給我們展現(xiàn)了 setTimeout 以及 setInterval,也看到了,我們可以通過 clearTimeout 或者 clearInterval 取消計(jì)時(shí)器,但是關(guān)于計(jì)時(shí)器,遠(yuǎn)遠(yuǎn)不止這點(diǎn)知識,請看下面的代碼(請):

const cancelImmediate = () => {
  const timerId = setTimeout(console.log, 0, "暫緩了 0 秒執(zhí)行")
  clearTimeout(timerId)
}

cancelImmediate() // 這里并不會有任何輸出

或者看下面這樣的代碼:

const cancelImmediate2 = () => setTimeout(console.log, 0, "暫緩了 0 秒執(zhí)行")

const timerId = cancelImmediate2()

clearTimeout(timerId)

請將上面的的任一代碼片段同時(shí)復(fù)制到瀏覽器的控制臺中(有多行復(fù)制多行)執(zhí)行,你會發(fā)現(xiàn),兩個代碼片段都沒有任何輸出,這是為什么?

這是因?yàn)椋琂avascript 的運(yùn)行機(jī)制導(dǎo)致,任何時(shí)刻都只能存在一個任務(wù)在進(jìn)行,雖然我們調(diào)用的是暫緩 0 秒,但是,由于當(dāng)前的任務(wù)還沒有執(zhí)行完成,所以,setTimeout 中被暫緩的函數(shù)即使時(shí)間到了也不會被執(zhí)行,必須等到當(dāng)前的任務(wù)完全執(zhí)行完成,那么,再試著,上面的代碼分行復(fù)制到控制臺,看看結(jié)果是不是會打印出 暫緩了 0 秒執(zhí)行 了?答案是肯定的。

當(dāng)你一行一行復(fù)制執(zhí)行的時(shí)候, cancelImmediate2 執(zhí)行完成之后,當(dāng)前任務(wù)就已經(jīng)全部執(zhí)行完成了,所以開始執(zhí)行下一個任務(wù)(console.log 開始執(zhí)行)。

從上面的示例中,我們可以看出,setTimeout 其實(shí)是將一個任務(wù)安排進(jìn)一個 Javascript 的任務(wù)隊(duì)列里面去,當(dāng)前面的所有任務(wù)都執(zhí)行完成之后,如果這個任務(wù)時(shí)間到了,那么就立即執(zhí)行,否則,繼續(xù)等待計(jì)時(shí)結(jié)束。

此時(shí),你應(yīng)該發(fā)現(xiàn),只要是 setTimeout 所暫緩的函數(shù)沒有被執(zhí)行(任務(wù)還沒有完成),那么,我們就可以隨時(shí)使用 clearTimeout 清除掉這個暫緩(將這條任務(wù)從隊(duì)列里面移除)

計(jì)時(shí)器是沒有任何保證的

通過前面的例子,我們知道了 setTimeoutdelay0 時(shí),并不表示立馬就會執(zhí)行了,它必須等到所有的當(dāng)前任務(wù)(對于一個 JS 文件來講,就是需要執(zhí)行完當(dāng)前腳本中的所有調(diào)用)執(zhí)行完成之后都會執(zhí)行,而這里面就包括我們調(diào)用的 clearTimeout

下面用一個示例來更清楚了說明這個問題:

setTimeout(console.log, 1000, "1 秒后執(zhí)行的")

// 開始時(shí)間
const startTime = new Date()
// 距離開始時(shí)間已經(jīng)過去幾秒
let secondsPassed = 0
while (true) {
  // 距離開始時(shí)間的毫秒數(shù)
  const duration = new Date() - startTime
  // 如果距離開始時(shí)間超過 5000 毫秒了, 則終止循環(huán)
  if (duration > 5000) {
    break
  } else {
    // 如果距離開始時(shí)間增長一秒,更新 secondsPassed
    if (Math.floor(duration / 1000) > secondsPassed) {
      secondsPassed = Math.floor(duration / 1000)
      console.log(`已經(jīng)過去 ${secondsPassed} 秒了。`)
    }
  }
}

你們猜上面這段代碼會有什么樣的輸出?是下面這樣的嗎?

1 秒后執(zhí)行的
已經(jīng)過去 1 秒了。
已經(jīng)過去 2 秒了。
已經(jīng)過去 3 秒了。
已經(jīng)過去 4 秒了。
已經(jīng)過去 5 秒了。

并不是這樣的,而是下面這樣的:

已經(jīng)過去 1 秒了。
已經(jīng)過去 2 秒了。
已經(jīng)過去 3 秒了。
已經(jīng)過去 4 秒了。
已經(jīng)過去 5 秒了。
1 秒后執(zhí)行的

怎么會這樣?這是因?yàn)?while(true) 這個循環(huán)必須要執(zhí)行超過 5 秒鐘的時(shí)間之后,才算當(dāng)前所有任務(wù)完成,在它 break 之前,其它所有的操作都是沒有用的,當(dāng)然,我們不會在開發(fā)的過程中去寫這樣的代碼,但是并不表示就不存在這樣的情況,想象以下下面這樣的場景:

setTimeout(somethingMustDoAfter1Seconds, 1000)

openFileSync("file more then 1gb")

這里面的 openFileSync 只是一個偽代碼,它表示我們需要同步進(jìn)行一個特別費(fèi)時(shí)的操作,這個操作很有可能會超過 1 秒,甚至更長的時(shí)間,但是上面那個 somethingMustDoAfter1Seconds 將一直處于掛起狀態(tài),只要這個操作完成,它才有可能執(zhí)行,為什么叫有可能?那是因?yàn)椋锌赡苓€有別的任務(wù)又會占用資源。所以,我們可以將 setTimeout 理解為:計(jì)時(shí)結(jié)束是執(zhí)行任務(wù)的必要條件,但是不是任務(wù)是否執(zhí)行的決定性因素

setTimeout(somethingMustDoAfter1Seconds, 1000) 的意思是,必須超過 1000 毫秒后,somethingMustDoAfter1Seconds 才允許執(zhí)行。

再來一個小挑戰(zhàn)

那如果我需要每一秒鐘都打印一句話怎么辦?從上面的示例中,已經(jīng)很明顯的看到了,setTimeout 是肯定解決不了這個問題了,不信我們可以試試下面這個代碼片段:

const log = (delay) => {
  timer = setTimeout(() => {
    console.log(`每 ${delay} 秒打印一次`)
    log(delay)
  }, delay * 1000)
}

log(1)

上面的代碼是沒有任何問題的,在瀏覽器的控制臺觀察,你會發(fā)現(xiàn)確實(shí)每一秒鐘都打印了一行,但是再試試下面這樣的代碼:

const log = (delay) => {
  timer = setTimeout(() => {
    console.log(`每 ${delay} 秒打印一次`)
    log(delay)
  }, delay * 1000)
}

const readLargeFileSync = () => {
  // 開始時(shí)間
  const startTime = new Date()
  // 距離開始時(shí)間已經(jīng)過去幾秒
  let secondsPassed = 0
  while (true) {
    // 距離開始時(shí)間的毫秒數(shù)
    const duration = new Date() - startTime
    // 如果距離開始時(shí)間超過 5000 毫秒了, 則終止循環(huán)
    if (duration > 5000) {
      break
    } else {
      // 如果距離開始時(shí)間增長一秒,更新 secondsPassed
      if (Math.floor(duration / 1000) > secondsPassed) {
        secondsPassed = Math.floor(duration / 1000)
        console.log(`已經(jīng)過去 ${secondsPassed} 秒了。`)
      }
    }
  }
}

log(1)

setTimeout(readLargeFileSync, 1300)

輸出結(jié)果是:

每 1 秒打印一次
已經(jīng)過去 1 秒了。
已經(jīng)過去 2 秒了。
已經(jīng)過去 3 秒了。
已經(jīng)過去 4 秒了。
已經(jīng)過去 5 秒了。
每 1 秒打印一次

第一秒的時(shí)候, log 執(zhí)行

第 1300 毫秒時(shí),開始執(zhí)行 readLargeFileSync 這會需要整整 5 秒鐘的時(shí)間

第 2 秒的時(shí)候,log 執(zhí)行時(shí)間到了,但是當(dāng)前任務(wù)并沒有完成,所以,它不會打印

第 5 秒的時(shí)候, readLargeFileSync 執(zhí)行完成了,所以 log 繼續(xù)執(zhí)行

關(guān)于這個具體怎么實(shí)現(xiàn),就不在本文討論了
最終,到底是誰在調(diào)用那個被暫緩的函數(shù)?

當(dāng)我們在一個 function 中調(diào)用 this 時(shí),this 關(guān)鍵字會指向當(dāng)前函數(shù)的 caller

function whoCallsMe() {
  console.log("My caller is: ", this)
}

當(dāng)我們在瀏覽器的控制臺中調(diào)用 whoCallsMe 時(shí),會打印出 Window,當(dāng)在 Node.js 的 REPL 中執(zhí)行時(shí),會執(zhí)行出 global,如果我們將 whoCallsMe 設(shè)置為一個對象的屬性:

function whoCallsMe() {
  console.log("My caller is: ", this)
}

const person = {
  name: "Tao Pan",
  whoCallsMe
}

person.whoCallsMe()

這會打印出:My caller is: Object { name: "Tao Pan", whoCallsMe: whoCallsMe() }

那么?

function whoCallsMe() {
  console.log("My caller is: ", this)
}

const person = {
  name: "Tao Pan",
  whoCallsMe
}

setTimeout(person.whoCallsMe, 0)

這會打印出什么?這個很容易被忽視的問題,其實(shí)真的值得我們?nèi)ニ伎肌?/p>

請直接將上面這個代碼片段復(fù)制進(jìn)瀏覽器的控制臺,看執(zhí)行的結(jié)果:

My caller is:  Window https://pantao.parcmg.com/admin/write-post.php?cid=2952

再打開系統(tǒng)終端,進(jìn)入 Node.js REPL 中,執(zhí)行同樣的代碼,看執(zhí)行結(jié)果:

My caller is:  Timeout {
  _idleTimeout: 1,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 7052,
  _onTimeout: [Function: whoCallsMe],
  _timerArgs: undefined,
  _repeat: null,
  _destroyed: false,
  [Symbol(refed)]: true,
  [Symbol(asyncId)]: 221,
  [Symbol(triggerId)]: 5
}

回到這句話:當(dāng)我們在一個 function 中調(diào)用 this 時(shí),this 關(guān)鍵字會指向當(dāng)前函數(shù)的 caller,當(dāng)我們使用 setTimeout 時(shí),這個 caller 是跟當(dāng)前的運(yùn)行時(shí)有關(guān)系的,如果我想 this 總是指向 person 對象呢?

function whoCallsMe() {
  console.log("My caller is: ", this)
}

const person = {
  name: "Tao Pan"
}
person.whoCallsMe = whoCallsMe.bind(person)

setTimeout(person.whoCallsMe, 0)
結(jié)語

標(biāo)題是寫上了 你需要知道的一切都在這里,但是如果有什么沒有考慮到了,歡迎大家指出。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106832.html

相關(guān)文章

  • 前端校招準(zhǔn)備系列--js中setTimeout到底是什么?

    摘要:瀏覽器是多進(jìn)程的,而瀏覽器的內(nèi)核渲染進(jìn)程是多線程的。如果已經(jīng)將回調(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列,但是主線程正在執(zhí)行一個非常耗時(shí)的任務(wù),當(dāng)這個任務(wù)執(zhí)行完畢后,主線程去任務(wù)隊(duì)列中取任務(wù),這個時(shí)候,就會出現(xiàn)連續(xù)執(zhí)行的情況,也就是說相當(dāng)于失效了。 前言 ??在刷筆試題的時(shí)候,經(jīng)常會碰到setTimeout的問題,只知道這個是設(shè)置定時(shí)器;但是考察的重點(diǎn)一般是在一個方法中包含了定時(shí)器,定時(shí)器中的打印和方法中打...

    Godtoy 評論0 收藏0
  • setTimeout深入JavaScript執(zhí)行環(huán)境異步機(jī)制

    摘要:圖片轉(zhuǎn)引自的演講和兩個定時(shí)器中回調(diào)的執(zhí)行邏輯便是典型的機(jī)制。異步編程關(guān)于異步編程我的理解是,在執(zhí)行環(huán)境所提供的異步機(jī)制之上,在應(yīng)用編碼層面上實(shí)現(xiàn)整體流程控制的異步風(fēng)格。 問題背景 在一次開發(fā)任務(wù)中,需要實(shí)現(xiàn)如下一個餅狀圖動畫,基于canvas進(jìn)行繪圖,但由于對于JS運(yùn)行環(huán)境中異步機(jī)制的不了解,所以遇到了一個棘手的問題,始終無法解決,之后在與同事交流之后才恍然大悟。問題的根節(jié)在于經(jīng)典的J...

    codeGoogle 評論0 收藏0
  • 徹底弄懂 JavaScript 執(zhí)行機(jī)制

    摘要:關(guān)于這部分有嚴(yán)格的文字定義,但本文的目的是用最小的學(xué)習(xí)成本徹底弄懂執(zhí)行機(jī)制,所以同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行場所,同步的進(jìn)入主線程,異步的進(jìn)入并注冊函數(shù)。宏任務(wù)微任務(wù)第三輪事件循環(huán)宏任務(wù)執(zhí)行結(jié)束,執(zhí)行兩個微任務(wù)和。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發(fā)工作,我們經(jīng)常會遇到這樣的情況:給定的幾行代碼,我們需要知道其輸出內(nèi)容和順序。 因?yàn)閖avascr...

    gyl_coder 評論0 收藏0
  • 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制

    摘要:事件完成,回調(diào)函數(shù)進(jìn)入。主線程從讀取回調(diào)函數(shù)并執(zhí)行。終于執(zhí)行完了,終于從進(jìn)入了主線程執(zhí)行。遇到,立即執(zhí)行。宏任務(wù)微任務(wù)第三輪事件循環(huán)宏任務(wù)執(zhí)行結(jié)束,執(zhí)行兩個微任務(wù)和。事件循環(huán)事件循環(huán)是實(shí)現(xiàn)異步的一種方法,也是的執(zhí)行機(jī)制。 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發(fā)工作...

    dreambei 評論0 收藏0
  • JavaScriptsetTimeoutsetInterval深入理解

    摘要:所以其實(shí)和所謂的異步調(diào)用事實(shí)上是通過將代碼段插入到代碼的執(zhí)行隊(duì)列中實(shí)現(xiàn)的。當(dāng)執(zhí)行和的時(shí)候,會根據(jù)你設(shè)定的時(shí)間準(zhǔn)確地找到代碼的插入點(diǎn)。綜上所述,其實(shí)終歸是單線程產(chǎn)物。無論如何異步都不可能突破單線程這個障礙。 發(fā)表過一片博客《跟著我用JavaScript寫計(jì)時(shí)器》,比較基礎(chǔ).....有網(wǎng)友說應(yīng)該寫一下setTimeout的原理和機(jī)制,嗯,今天就來寫一下吧: 直奔主題:setTimeout和...

    myeveryheart 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<