<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你不知道的JavaScript計(jì)時器知識

          共 6534字,需瀏覽 14分鐘

           ·

          2021-01-12 04:56

          本文系翻譯,下面正文開始!??

          幾周之前,我在推特上發(fā)了這個面試題:

          在開始之前,先在你腦海中回答這個問題

          推特上大約一半的回答都是錯誤的。答案并不是 V8(或者虛擬機(jī))!!雖然“JavaScript”計(jì)時器” 很出名,但是 setTimeoutsetInterval 函數(shù)并不是 ECMAScript 規(guī)范或者任何 JavaScript 引擎實(shí)現(xiàn)。定時器由瀏覽器實(shí)現(xiàn),在不同瀏覽器中的實(shí)現(xiàn)也會有所不同,Node.js 也實(shí)現(xiàn)了自己的定時器。

          在瀏覽器中,主計(jì)時器函數(shù)是 Window 接口的一部分,它具有一些其他函數(shù)和對象。該接口使其所有元素在主 JavaScript 全局可用。這就是您可以直接在瀏覽器控制臺中執(zhí)行 setTimeout 的原因。

          在 Node 中,計(jì)時器是 global對象的一部分,其行為類似于瀏覽器的 Window 接口。你可以在此處查看 Node 中的計(jì)時器源代碼(https://github.com/nodejs/node/blob/master/lib/timers.js)。

          有些人可能認(rèn)為這是一個糟糕的面試問題,為什么要知道這個問題呢?!作為一名 JavaScript 開發(fā)人員,我認(rèn)為你應(yīng)該知道這一點(diǎn),因?yàn)槿绻悴贿@樣做,那可能表明你并不完全理解 V8(和其他虛擬機(jī))如何與瀏覽器和 Node 交互。

          讓我們舉幾個關(guān)于計(jì)時器功能的例子和挑戰(zhàn),準(zhǔn)備好了嗎?

          更新:這篇文章現(xiàn)在是我的“Complete Introduction to Node.js”的一部分。您可以在此處閱讀更新版本。(https://jscomplete.com/g/js-timers)

          延遲函數(shù)的執(zhí)行

          定時器函數(shù)是高階函數(shù),可用于延遲或重復(fù)執(zhí)行其他函數(shù)(它們作為第一個參數(shù)接收)。

          這是一個關(guān)于延遲的例子:

          1. // example1.js

          2. setTimeout(() => {

          3. console.log("Hello after 4 seconds");

          4. }, 4 * 1000);

          此示例使用 setTimeout 將問候消息的打印延遲 4 秒。setTimeout 的第二個參數(shù)是延遲(以 ms 為單位)。這就是為什么我將 4 乘以 1000 使其成為 4 秒

          setTimeout 的第一個參數(shù)是執(zhí)行將被延遲的函數(shù)。

          如果使用 node 命令執(zhí)行 example1.js 文件,Node 將暫停 4 秒鐘,然后它將打印問候語消息(并在此之后退出)。

          請注意, setTimeout 的第一個參數(shù)只是一個函數(shù)引用。它不必像 example1.js 那樣是內(nèi)聯(lián)函數(shù)。這是不使用內(nèi)聯(lián)函數(shù)的相同示例:

          1. const func = () => {

          2. console.log("Hello after 4 seconds");

          3. };

          4. setTimeout(func, 4 * 1000);

          傳遞參數(shù)

          如果使用 setTimeout 延遲其執(zhí)行的函數(shù)接受任何參數(shù),我們可以使用 setTimeout 本身的剩余參數(shù)(在我們了解到目前為止的 2 參數(shù)之后)將參數(shù)值中繼到延遲函數(shù)。

          1. // For: func(arg1, arg2, arg3, ...)

          2. // We can use: setTimeout(func, delay, arg1, arg2, arg3, ...)

          下面是一個例子:

          1. // example2.js

          2. const rocks = who => {

          3. console.log(who + " rocks");

          4. };

          5. setTimeout(rocks, 2 * 1000, "Node.js");

          上面的 rock 函數(shù)延遲了 2 秒,接受了一個 who 參數(shù),并且 setTimeout 調(diào)用將值“Node.js”作為參數(shù)傳遞給 rock。

          使用 node 命令執(zhí)行 example2.js 將在 2 秒后打印出“Node.js rocks”。

          定時器挑戰(zhàn)#1

          使用您到目前為止學(xué)到的有關(guān) setTimeout 的知識,在相應(yīng)的延遲后打印以下 2 條消息。

          • 4 秒后打印消息“4 秒后你好”

          • 8 秒后打印消息“8 秒后你好”。

          條件:

          您只能在解決方案中定義一個函數(shù),其中包括內(nèi)聯(lián)函數(shù)。這意味著多個 setTimeout 調(diào)用必須使用完全相同的函數(shù)。

          解答

          以下是我如何解決這一挑戰(zhàn):

          1. const theOneFunc = delay => {

          2. console.log("Hello after " + delay + " seconds");

          3. };

          4. setTimeout(theOneFunc, 4 * 1000, 4);

          5. setTimeout(theOneFunc, 8 * 1000, 8);

          我讓 theOneFunc 收到一個延遲參數(shù),并在打印的消息中使用了該 delay 參數(shù)的值。這樣,該函數(shù)可以根據(jù)我們傳遞給它的任何延遲值打印不同的消息。

          然后我在兩個 setTimeout 調(diào)用中使用了 theOneFunc ,一個在 4 秒后觸發(fā),另一個在 8 秒后觸發(fā)。這兩個 setTimeout 調(diào)用也通過第三個參數(shù)來表示 theOneFunc 的延遲參數(shù)。

          使用 node 命令執(zhí)行 solution1.js 文件將打印出我們挑戰(zhàn)的要求,4 秒后的第一條消息和 8 秒后的第二條消息。

          重復(fù)執(zhí)行一個函數(shù)

          如果我要求您每隔 4 秒打印一條消息怎么辦?

          雖然您可以將 setTimeout 放在循環(huán)中,但定時器 API 也提供了 setInterval 函數(shù),這將完成永遠(yuǎn)執(zhí)行某些操作的要求。

          這是 setInterval 的一個例子:

          1. // example3.js

          2. setInterval(() => console.log("Hello every 3 seconds"), 3000);

          結(jié)束定時器

          因?yàn)檎{(diào)用計(jì)時器函數(shù)是一個調(diào)度操作,所以在執(zhí)行之前也可以取消該調(diào)度操作。

          setTimeout 的調(diào)用返回一個計(jì)時器“ID”,您可以使用帶有 clearTimeout 調(diào)用的計(jì)時器 ID 來取消該計(jì)時器。這是一個例子:

          1. // example4.js

          2. const timerId = setTimeout(

          3. () => console.log('You will not see this one!'),

          4. 0

          5. );

          6. clearTimeout(timerId);

          這個簡單的計(jì)時器應(yīng)該在 0 毫秒后立即啟動,但它并沒有按照我們預(yù)期的那樣,因?yàn)槲覀円呀?jīng)捕獲 timerId值并在使用 clearTimeout 調(diào)用后立即取消它。

          當(dāng)我們通過 node命令去執(zhí)行 example4.js,Node 將不會打印任何信息并且退出進(jìn)程。

          順便說一句,在 Node.js 中,還有另一種方法可以使用 0 ms 進(jìn)行 setTimeout 。Node.js 計(jì)時器 API 有另一個名為 setImmediate 的函數(shù),它與一個 0 ms 的 setTimeout 基本相同,但我們不必在那里指定延遲:

          1. setImmediate(() => console.log("I am equivalent to setTimeout with 0 ms"));

          _ setImmediate 函數(shù)不是在所有的瀏覽器都是可用的(https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate),不要在客戶端的代碼中使用這個_。

          就像 clearTimeout 一樣,還有一個 clearInterval 函數(shù),它對 setInerval 調(diào)用執(zhí)行相同的操作,并且還有一個 clearImmediate 也調(diào)用同樣的操作。

          定時器延遲不是固定的

          在前面的例子中,您是否注意到在 0 ms 之后執(zhí)行 setTimeout 的操作并不意味著立即執(zhí)行它(在 setTimeout內(nèi)部),而是在腳本中的所有其他操作之后立即執(zhí)行它(包括 clearTimeout 調(diào)用)?

          讓我用一個例子清楚地說明這一點(diǎn)。這是一個簡單的 setTimeout 調(diào)用,應(yīng)該在半秒后觸發(fā),但它不會:

          1. // example5.js

          2. setTimeout(() => console.log("Hello after 0.5 seconds. MAYBE!"), 500);

          3. for (let i = 0; i < 1e10; i++) {

          4. // Block Things Synchronously

          5. }

          在此示例中定義計(jì)時器之后,我們使用 big for 循環(huán)同步阻止運(yùn)行。1e1010的十次方,所以循環(huán)是一個 10 億個循環(huán)(基本上模擬繁忙的 CPU)。當(dāng)此循環(huán)正在滴答時,節(jié)點(diǎn)無法執(zhí)行任何操作。

          這當(dāng)然在實(shí)際是很糟糕的,但它會幫助你理解 setTimeout 延遲不是一個保證的東西,而是一個最小的事情。500 ms 表示最小延遲為 500 ms。實(shí)際上,腳本將花費(fèi)更長的時間來打印其問候語。它必須等待阻塞循環(huán)才能完成

          定時器挑戰(zhàn)#2

          編寫腳本每秒打印消息“Hello World”,但只打印 5 次。5 次后,腳本應(yīng)打印消息“完成”并讓 Node 進(jìn)程退出。

          約束:您不能對此挑戰(zhàn)使用 setTimeout 調(diào)用。提示:你需要一個計(jì)數(shù)器。

          解答

          下面是我的解決方案:

          1. let counter = 0;

          2. const intervalId = setInterval(() => {

          3. console.log("Hello World");

          4. counter += 1;

          5. if (counter === 5) {

          6. console.log("Done");

          7. clearInterval(intervalId);

          8. }

          9. }, 1000);

          我初始化 counter0 ,然后啟動一個 setInterval 調(diào)用并捕獲了它的 id。

          延遲函數(shù)將會打印消息并每次遞增計(jì)數(shù)器。在延遲函數(shù)內(nèi),if 語句將檢查我們現(xiàn)在是否處于 5 次。如果是這樣,它將打印“Done”并使用捕獲的 intervalId 常量清除間隔。間隔延遲為 1000 毫秒

          究竟誰“調(diào)用”延遲函數(shù)?

          當(dāng)您在常規(guī)函數(shù)中使用 JavaScript this 關(guān)鍵字時,如下所示:

          1. function whoCalledMe() {

          2. console.log("Caller is", this);

          3. }

          函數(shù)內(nèi)部 this 關(guān)鍵字的值表示函數(shù)的調(diào)用者。如果在 Node REPL 中定義上面的函數(shù),則調(diào)用者將是 global對象。如果在瀏覽器控制臺中定義函數(shù),則調(diào)用者將是 window 對象。

          讓我們定義一個函數(shù)作為一個對象的屬性,為了更清晰的理解 this

          1. const obj = {

          2. id: "42",

          3. whoCalledMe() {

          4. console.log("Caller is", this);

          5. }

          6. };

          7. // The function reference is now: obj.whoCallMe

          現(xiàn)在當(dāng)您直接通過其引用去調(diào)用 obj.whoCallMe 函數(shù)時,調(diào)用者將是 obj 對象(由其 id 標(biāo)識):

          現(xiàn)在問題是,如果我們將 obj.whoCallMe 的引用傳遞給 setTimetout 調(diào)用,調(diào)用者會是什么?

          1. // What will this print??

          2. setTimeout(obj.whoCalledMe, 0);

          誰才是真正的調(diào)用者?

          答案是:根據(jù)執(zhí)行計(jì)時器功能的位置而有所不同。在這個例子中,你無法直接判斷誰是調(diào)用者。因?yàn)槎〞r器實(shí)現(xiàn)是將你的函數(shù)喚醒。如果您在 Node REPL 中測試它,您將獲得一個 Timetout 對象作為調(diào)用者:

          請注意,這只在您在常規(guī)函數(shù)中使用 JavaScript 的 this 關(guān)鍵字時才有意義。如果您使用箭頭功能,則根本不需要擔(dān)心調(diào)用者。

          定時器挑戰(zhàn)#3

          編寫腳本以連續(xù)打印具有不同延遲的消息“Hello World”。以 1 秒的延遲開始,然后每次將延遲增加 1 秒。第二次將延遲 2 秒。第三次將延遲 3 秒,依此類推。

          在打印的消息中包含延遲。預(yù)期輸出看起來像:

          1. Hello World. 1

          2. Hello World. 2

          3. Hello World. 3

          4. ...

          約束:您只能使用 const 來定義。你不能使用 let 或 var。

          解答

          因?yàn)檠舆t量是此挑戰(zhàn)中的變量,所以我們不能在這里使用 setInterval ,但我們可以在遞歸調(diào)用中使用 setTimeout 手動創(chuàng)建間隔執(zhí)行。使用 setTimeout 的第一個執(zhí)行函數(shù)將創(chuàng)建另一個計(jì)時器,依此類推。

          另外,因?yàn)槲覀儾荒苁褂?let / var,所以我們不能有一個計(jì)數(shù)器來增加每個遞歸調(diào)用的延遲,但我們可以使用遞歸函數(shù)參數(shù)在遞歸調(diào)用期間遞增。

          這是解決這一挑戰(zhàn)的一種可能方法:

          1. const greeting = delay =>

          2. setTimeout(() => {

          3. console.log("Hello World. " + delay);

          4. greeting(delay + 1);

          5. }, delay * 1000);

          6. greeting(1);

          定時器挑戰(zhàn)#4

          編寫一個腳本以連續(xù)打印消息“Hello World”,其具有與 challenge#3 相同的變化延遲概念,但這次是每個主延遲間隔的 5 個消息組。從前 5 個消息的延遲 100ms 開始,接下來的 5 個消息延遲 200ms,然后是 300ms,依此類推。

          以下是腳本的行為方式:

          • 在 100ms 點(diǎn),腳本將開始打印“Hello World”,并以 100ms 的間隔進(jìn)行 5 次。第一條消息將顯示為 100 毫秒,第二條消息將顯示為 200 毫秒,依此類推。

          • 在前 5 條消息之后,腳本應(yīng)將主延遲增加到 200ms。因此,第 6 條消息將以 500 毫秒+ 200 毫秒(700 毫秒)打印,第 7 條消息將以 900 毫秒打印,第 8 條消息將以 1100 毫秒打印,依此類推。

          • 在 10 條消息之后,腳本應(yīng)將主延遲增加到 300 毫秒。所以第 11 條消息應(yīng)該以 500ms + 1000ms + 300ms(18000ms)打印。第 12 條消息應(yīng)打印在 21000ms,依此類推。

          • 按照這個模式無限執(zhí)行下去。

          在打印的消息中包含延遲。預(yù)期的輸出看起來像這樣(沒有注釋):

          1. Hello World. 100 // At 100ms

          2. Hello World. 100 // At 200ms

          3. Hello World. 100 // At 300ms

          4. Hello World. 100 // At 400ms

          5. Hello World. 100 // At 500ms

          6. Hello World. 200 // At 700ms

          7. Hello World. 200 // At 900ms

          8. Hello World. 200 // At 1100ms

          約束:您只能使用 setInterval 調(diào)用(而不是 setTimeout),并且只能使用 ONEif 語句。

          解答

          因?yàn)槲覀冎荒苁褂?setInterval 調(diào)用,所以我們還需要遞歸,以增加下一個 setInterval調(diào)用的延遲。另外,我們需要一個 if 語句來控制只有在 5 次調(diào)用該遞歸函數(shù)之后才能執(zhí)行此操作。

          這是一個可能的解決方案:

          1. let lastIntervalId,

          2. counter = 5;

          3. const greeting = delay => {

          4. if (counter === 5) {

          5. clearInterval(lastIntervalId);

          6. lastIntervalId = setInterval(() => {

          7. console.log("Hello World. ", delay);

          8. greeting(delay + 100);

          9. }, delay);

          10. counter = 0;

          11. }

          12. counter += 1;

          13. };

          14. greeting(100);

          如果您剛剛開始學(xué)習(xí) Node.js,我最近出版了first-steps course at Pluralsight,請查看:

          https://jscomplete.com/c/nodejs-getting-started

          原文鏈接:https://medium.freecodecamp.org/javascript-timers-everything-you-need-to-know-5f31eaa37162

          瀏覽 79
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野结衣国产42区 | xxx国产在线免费观看 | 国产一卡二卡在线观看 | 北条麻美在线无码 | 国产精品精品国产婷婷这里Aⅴ |