搜尋
首頁web前端js教程Node.js中使用計時器定時執行函數詳解_node.js

如果你熟悉客戶端JavaScript編程,你可能使用過setTimeout和setInterval函數,這兩個函數允許延時一段時間再運行函數。例如下面的程式碼, 一旦被載入到Web頁面,1秒後會在頁面文件後追加「Hello there」:

複製程式碼 程式碼如下:

var oneSecond = 1000 * 1; // one second = 1000 x 1 ms

setTimeout(function() {

    document.write('

Hello there.

');

}, oneSecond);

而setInterval允許以指定的時間間隔重複執行函數。如果把下面的程式碼注入到Web頁面,會導致每秒鐘在頁面文件後面追加一句「Hello there」:

複製程式碼 程式碼如下:

                  var oneSecond = 1000 * 1; // one second = 1000 x 1 ms

                  setInterval(function() {

                                  

                  }, oneSecond);


因為Web早已成為用來建立應用程式的平台,而不再是簡單的靜態頁面,所以這種類似的需求日益浮現。這些任務規劃函數可協助開發人員實現表單定期驗證,延遲遠端資料同步,或是需要延遲反應的UI互動。 Node也完整實作了這些方法。在伺服器端,你可以用它們來重複或延遲執行很多任務,例如快取過期,連接池清理,會話過期,輪詢等等。

使用setTimeout延遲函數執行

setTimeout可以製定一個在將來某個時間把指定函數運行一次的執行計劃,例如:

複製程式碼 程式碼如下:
                   var timeout_ms = 2000; // 2 seconds
                   var timeout = setTimeout(function() {

                            console.log("timed out!");

                   }, timeout_ms);


和客戶端JavaScript完全一樣,setTimeout接受兩個參數,第一個參數是需要被延遲的函數,第二個參數是延遲時間(以毫秒為單位)。

setTimeout回傳一個超時句柄,它是個內部對象,可以用它作為參數呼叫clearTimeout來取消計時器,除此之外這個句柄沒有任何作用。

使用clearTimeout取消執行計畫

一旦獲得了超時句柄,就可以用clearTimeout來取消函數執行計劃,像這樣:


複製程式碼 程式碼如下:
                   var timeoutTime = 1000; // one second
                   var timeout = setTimeout(function() {

                            console.log("timed out!");

                   }, timeoutTime);

                   clearTimeout(timeout);


 這個例子裡,計時器永遠不會被觸發,也不會輸出」time out!」這幾個字。你也可以在將來的任何時間取消執行計劃,就像下面的例子:

複製程式碼 程式碼如下:

 var timeout = setTimeout(function A() {
 
                            console.log("timed out!");
 
                   }, 2000);
 
                   setTimeout(function B() {
 
                            而clearTimeout(timeout);
 
                   }, 1000);

程式碼指定了兩個延遲執行的函數A和B,函數A計劃在2秒鐘後執行,B計劃在1秒鐘後執行,因為函數B先執行,而它取消了A的執行計劃,因此A永遠不會運作。

制定與取消函數的重複執行計畫

setInterval和setTimeout類似,但它會以指定時間為間隔重複執行一個函數。你可以用它來週期性的觸發一段程序,來完成一些類似清理,收集,日誌,獲取數據,輪詢等其它需要重複執行的任務。

下面程式碼每秒會向控制台輸出一句「tick」:

複製程式碼 程式碼如下:

                   var period = 1000; // 1 second

                   setInterval(function() {

                            console.log("tick");

                   }, period);

如果你不想讓它永遠運作下去,可以用clearInterval()取消計時器。

setInterval回傳一個執行計畫句柄,可以把它當作clearInterval的參數來取消執行計畫:

複製程式碼 程式碼如下:

                   var interval = setInterval(function() {

                            console.log("tick");

                   }, 1000);

                   // …

                   clearInterval(interval);

使用process.nextTick將函數執行延遲到事件循環的下一輪

有時客戶端JavaScript程式設計師用setTimeout(callback,0)將任務延遲一段很短的時間,第二個參數是0毫秒,它告訴JavaScript運行時,當所有掛起的事件處理完畢後立刻執行這個回調函數。有時候這種技術被用來延遲執行一些並不需要立刻執行的操作。例如,有時候需要在使用者事件處理完畢後再開始播放動畫或做一些其它的計算。

Node中,就像 「事件循環」的字面意思,事件循環運行在一個處理事件佇列的循環裡,事件循環工作過程中的每一輪就稱為一個tick。

你可以在事件循環每次開始下一輪(下一個tick)執行時調用回調函數一次,這也正是process.nextTick的原理,而setTimeout,setTimeout使用JavaScript運行時內部的執行隊列,而不是使用事件循環。

透過使用process.nextTick(callback) ,而不是setTimeout(callback, 0),你的回呼函數會在佇列內的事件處理完畢後立刻執行,它要比JavaScript的超時佇列快很多(以CPU時間來衡量)。

你可以像下面這樣,把函數延遲到下一輪事件循環再運行:

複製程式碼 程式碼如下:

                   process.nextTick(function() {

                           my_expensive_computation_function();

                   });

  注意:process物件是Node少數的全域物件之一。

阻塞事件循環

Node和JavaScript的執行時間採用的是單執行緒事件循環,每次循環,執行時透過呼叫相關回呼函數來處理佇列內的下個事件。當事件執行完畢,事件循環取得執行結果並處理下個事件,如此反复,直到事件隊列為空。如果其中一個回呼函數運行時佔用了很長時間,事件循環在那段時間就無法處理其它掛起的事件,這會讓應用程式或服務變得非常慢。

在處理事件時,如果使用了記憶體敏感或處理器敏感的函數,會導致事件循環變得緩慢,而且造成大量事件堆積,不能被及時處理,甚至堵塞隊列。

看下面阻塞事件循環的例子:

複製程式碼 程式碼如下:

                   process.nextTick(function nextTick1() {

                            var a = 0;

                            while(true) {

                                  

                            }

                   });

                   process.nextTick(function nextTick2() {

                            console.log("next tick");

                   });

                   setTimeout(function timeout() {

                            console.log("timeout");

                   }, 1000);


這個例子裡,nextTick2和timeout函數無論等待多久都沒機會運行,因為事件循環被nextTick函數裡的無限循環堵塞了,即使timeout函數被計劃在1秒鐘後執行它也不會運行。

         當使用setTimeout時,回呼函數會被加入到執行計畫佇列,而在這個例子裡它們甚至不會被加入到佇列。這雖然是個極端例子,但你可以看到,執行一個處理器敏感的任務時可能會堵塞或拖慢事件循環。

退出事件循環

使用process.nextTick,可以把一個非關鍵性的任務推遲到事件循環的下一輪(tick)再執行,這樣可以釋放事件循環,讓它可以繼續執行其它掛起的事件。

看下面例子,如果你打算刪除一個臨時文件,但是又不想讓data事件的回調函數等待這個IO操作,你可以這樣延遲它:


複製程式碼 程式碼如下:
                   stream.on("data", function(data) {
                           stream.end("my response");

                            process.nextTick(function() {

                                   

                            });

                   });


使用setTimeout取代setInterval來確保函數執行的串列性

假設,你打算設計一個叫my_async_function的函數,它可以做某些I/O操作(例如解析日誌檔)的函數,並打算讓它週期性執行,你可以用setInterval這樣實現它:

複製程式碼 程式碼如下:

                   var interval = 1000;

                   setInterval(function() {

                            my_async_function(function() {

                                   

                            });

                   },interval);//譯者註:前面「,interval」是我新增的,作者應該是錯誤遺漏了


你必須能確保這些函數不會同時執行,但是如果使用setinterval你無法保證這一點,假如my_async_function函數運行的時間比interval變數多了一毫秒,它們就會同時執行,而不是按次序串行執行。

譯者註:(下方粗體部分為譯者添加,非原書內容)

為了方便理解這部分內容,可以修改下作者的程式碼,讓它可以實際運作:

複製程式碼 程式碼如下:
                   var interval = 1000;
                   setInterval(function(){

                            (function my_async_function(){

                               

                                  

                                  

                           })();

                   },interval);

 運行下這段程式碼看看,你會發現,等待5秒鐘後,「hello 」被每隔1秒輸出一次。而我們期望是,當前my_async_function執行完畢(耗費5秒)後,等待1秒再執行下一個my_async_function,每次輸出之間應該間隔6秒才對。造成這種結果,是因為my_async_function不是串行執行的,而是多個在同時運行。

 因此,你需要一個辦法來強制使一個my_async_function執行結束到下個my_async_function開始執行之間的間隔時間正好是interval變數指定的時間。你可以這樣做:  


複製程式碼

程式碼如下:

                    var interval = 1000; // 1 秒

                   (function schedule() {      //第3行

                           以setTimeout(function do_it() {

                                  

                                  

                                  

                                  

                            }, interval);

                   }());        //第10排

 

前面程式碼裡,宣告了一個叫schedule的函數(第3行),並且在宣告後立刻呼叫它(第10行),schedule函數會在1秒(由interval指定)後執行do_it函式。 1秒鐘過後,第5行的my_async_function函數會被調用,當它執行完畢後,會調用它自己的那個匿名回調函數(第6行),而這個匿名回調函數又會再次重置do_it的執行計劃,讓它1秒鐘後重新執行,這樣程式碼就開始串行地不斷循環執行了。

小結

可以用setTimeout()函數預先設定函數的執行計劃,並用clearTimeout()函數取消它。也可以用setInterval()週期性的重複執行某個函數,對應的,可以使用clearInterval()取消這個重複執行計劃。 如果因為使用了一個處理器敏感的操作而堵塞了事件循環,那些原計劃應該被執行的函數將會被延遲,甚至永遠無法執行。所以不要在事件循環內使用CPU敏感的操作。還有,你可以用process.nextTick()把函數的執行延遲到事件循環的下一輪。

I/O和setInterval()一起使用時,你無法保證在任何時間點只有一個掛起的調用,但是,你可以使用遞歸函數和setTimeout()函數來迴避這個棘手的問題。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器