摘要:如果當前沒有事件也沒有定時器事件,則返回。相關資料關于的架構及設計思路的事件討論了使用線程池異步運行代碼。下一篇初窺事件機制的實現二中定時器的實現
在瀏覽器中,事件作為一個極為重要的機制,給予JavaScript響應用戶操作與DOM變化的能力;在Node.js中,事件驅動模型則是其高并發能力的基礎。
學習JavaScript也需要了解它的運行平臺,為了更好的理解JavaScript的事件模型,我打算從Node及瀏覽器引擎源碼入手,分析其底層實現,并將我的分析整理為一系列博文;一方面作為筆記,另一方面也希望能與大家交流,分析和理解有疏漏偏頗之處,還望各位斧正。
簡述事件驅動模型解釋JavaScript事件模型本身的好文章已經很多了,可以說這已經是一個說爛了的話題,這里我只簡單寫一下,并且提供一些好文章的鏈接。
程序如何響應事件我們的程序響應外部的事件有如下兩種方式:
中斷
操作系統處理鍵盤等硬件輸入就是通過中斷來進行的,這個方式的好處是即使沒有多線程,我們也可以放心地執行我們的代碼,CPU收到中斷信號之后自動地轉去執行相應的中斷處理程序,處理完成后會恢復原來的代碼的執行環境繼續執行。這種方式需要硬件的支持,一般來說都會被操作系統封裝起來。
輪詢
循環檢測是否有事件發生,如果有就去執行相應的處理程序。這在底層和上層的開發中都有應用。
Windows窗口程序就需要在主線程中寫下如下代碼,通常稱做消息循環:
MSG msg = { }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
消息循環不斷檢測是否有消息(用戶的UI操作、系統消息等)出現,有的話就分發消息,調用相應的回調函數進行處理。
輪詢方式的一個缺點就是:如果在主線程的消息循環里進行耗時操作,程序就無法及時響應新的消息。這在JavaScript中表現明顯,以后還會提到這一點,并探討其解決方案。
然而JavaScript中并沒有類似消息循環代碼,我們只是簡單地注冊事件,然后等待被調用。這是因為瀏覽器、Node作為執行平臺,已經將event loop實現了,JavaScript代碼不需要介入到這個過程中,只需要作為被調用者安靜地等待即可。
相關討論知乎-關于瀏覽器處理事件的問題?匿名用戶的回答:這個回答里圖很不錯,有助于理解event loop的工作原理;答案末尾有一些文章分享;
MDN - Concurrency model and Event Loop:MDN上對event loop的介紹。
Node中的event loop 通過Node源碼看event loop的實現Node采用V8作為JavaScript的執行引擎,同時使用libuv實現事件驅動式異步I/O。其事件循環就是采用了libuv的默認事件循環。
在src/node.cc中,
Environment* env = CreateEnvironment( node_isolate, uv_default_loop(), context, argc, argv, exec_argc, exec_argv);
這段代碼建立了一個node執行環境,可以看到第三行的uv_default_loop(),這是libuv庫中的一個函數,它會初始化uv庫本身以及其中的default_loop_struct,并返回一個指向它的指針default_loop_ptr。
之后,Node會載入執行環境并完成一些設置操作,然后啟動event loop:
bool more; do { more = uv_run(env->event_loop(), UV_RUN_ONCE); if (more == false) { EmitBeforeExit(env); // Emit `beforeExit` if the loop became alive either after emitting // event, or after running some callbacks. more = uv_loop_alive(env->event_loop()); if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) more = true; } } while (more == true); code = EmitExit(env); RunAtExit(env); ...
more用來標識是否進行下一輪循環。
env->event_loop()會返回之前保存在env中的default_loop_ptr,uv_run函數將以指定的UV_RUN_ONCE模式啟動libuv的event loop。在這種模式下,uv_run會至少處理一個事件:這意味著,如果當前事件隊列中沒有需要處理的I/O事件,uv_run會阻塞住,直到有I/O事件需要處理,或者下一個定時器時間到。如果當前沒有I/O事件也沒有定時器事件,則uv_run返回false。
接下來Node會根據more的情況決定下一步操作:
如果more為true,則繼續運行下一輪loop。
如果more為false,說明已經沒有等待處理的事件了,EmitBeforeExit(env);觸發進程的"beforeExit"事件,檢查并處理相應的處理函數,完成后直接跳出循環。
最后觸發"exit"事件,執行相應的回調函數,Node運行結束,后面會進行一些資源釋放操作。
在libuv中,event loop會在每次循環的開始更新自己的time從而實現計時功能,而I/O事件則分為兩類:
Network I/O是使用系統提供的非阻塞式I/O解決方案,例如在Linux上使用epoll,windows上使用IOCP。
文件操作和DNS操作沒有(很好的)系統解決方案,因此libuv自建了線程池,在其中進行阻塞式I/O。
另外我們也可以將自定義的函數拋到線程池中運行,在運行結束后主線程會執行相應的回調函數,不過Node并沒有將這一項功能加入到JavaScript中,也就是說只用原生Node是無法在JavaScript中開啟新的線程進行并行執行的。
相關資料libuv Design Overview:關于libuv的架構及設計思路;
node child_process "exit":Node的child_process "exit"事件;
Node with threads:討論了使用libuv線程池異步運行JavaScript代碼。
下一篇: 初窺JavaScript事件機制的實現(二)—— Node.js中定時器的實現
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/85737.html
摘要:下面通過幾個的定時器示例以及相關源碼來分析在中,功能到底是怎么實現的。我們知道,中的定時器并不同于計算機底層的定時中斷。補充資料在高級程序設計第三版第章高級技巧中對高級定時器以及有較詳細的討論。至此,這類定時器函數已經可以為所用了。 上一篇博文提到,在Node中timer并不是通過新開線程來實現的,而是直接在event loop中完成。下面通過幾個JavaScript的定時器示例以及N...
摘要:文件系統請求和相關請求都會放進這個線程池處理其他的請求,如網絡平臺特性相關的請求會分發給相應的系統處理單元參見設計概覽。 譯者按:在 Medium 上看到這篇文章,行文脈絡清晰,闡述簡明利落,果斷點下翻譯按鈕。第一小節背景鋪陳略啰嗦,可以略過。剛開始我給這部分留了個 blah blah blah 直接翻后面的,翻完之后回頭看,考慮完整性才把第一節給補上。接下來的內容干貨滿滿,相信對 N...
原文 先說1.1總攬: Reactor模式 Reactor模式中的協調機制Event Loop Reactor模式中的事件分離器Event Demultiplexer 一些Event Demultiplexer處理不了的復雜I/O接口比如File I/O、DNS等 復雜I/O的解決方案 未完待續 前言 nodejs和其他編程平臺的區別在于如何去處理I/O接口,我們聽一個人介紹nodejs,總是...
摘要:階段有兩個主要功能也會執行時間定時器到達期望時間的回調函數執行事件循環列表里的函數當進入階段并且沒有其余的定時器,那么如果事件循環列表不為空,則迭代同步的執行隊列中的函數。如果沒有,則等待回調函數進入隊列并立即執行。 Event Loop 本文以 Node.js 為例,講解 Event Loop 在 Node.js 的實現,原文,JavaScript 中的實現大同小異。 什么是 Eve...
摘要:概述本文主要介紹了我對的一些核心特性的理解,包括架構特點機制核心模塊與簡單應用。在此期間,主線程繼續執行其他任務。延續了瀏覽器端單線程,只用一個主線程執行,不斷循環遍歷事件隊列,執行事件。 原文地址在我的博客,轉載請注明來源,謝謝! node是在前端領域經常看到的詞。node對于前端的重要性已經不言而喻,掌握node也是作為合格的前端工程師一項基本功了。知道node、知道后端的一些東西...
閱讀 846·2021-10-25 09:48
閱讀 617·2021-08-23 09:45
閱讀 2509·2019-08-30 15:53
閱讀 1765·2019-08-30 12:45
閱讀 608·2019-08-29 17:21
閱讀 3423·2019-08-27 10:56
閱讀 2557·2019-08-26 13:48
閱讀 704·2019-08-26 12:24