摘要:既然這樣,怎么理解中的單線程再捋一捋和的關系。在線程上,不會等待操作完成,繼續執行后續的代碼。這就是單線程異步。在中除了代碼,一切都是并行的由于中主任務的執行是以單線程的方式進行,如果程序出錯導致崩潰,就會終止整個流程。
node是什么
第一句話Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環境。
Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型。
Node使用包管理器NPM。
Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環境。
運行環境, 不是一門語言,不是一個框架。只是能夠作為JavaScript代碼運行的一個環境。
而這個運行環境主要是由V8提供的。
V8做了什么?創建了一個callstack。
function main(){ func1(); } function func1(){ func2(); } function func2(){ console.log(1); } main();除去V8,Node中還有哪些東西?
除去V8,Node中另外一個比較重要的組成就是 libuv。
What? libuv是什么鬼?
先說說,關于Node的另外一句話:
Node is designed to build scalable network applications.
這句話的底氣在哪兒,就是Node本身采用的 事件驅動,非阻塞I/O模型。
在 并發模型構建網絡的應用中,每個連接都會生成一個新線程,每個新線程可能需要 2MB 的配套內存。在一個擁有 8 GB RAM 的系統上,理論上最大的并發連接數量是 4,000 個用戶。隨著您的客戶群的增長,如果希望您的 Web 應用程序支持更多用戶,那么,您必須添加更多服務器。所以在傳統的后臺開發中,整個 Web 應用程序架構(包括流量、處理器速度和內存速度)中的瓶頸是:服務器能夠處理的并發連接的最大數量。這個不同的架構承載的并發數量是不一致的。而且,多個線程存在的話,也會衍生出線程切換的問題,這也會占用一定資源。
并發存在的兩個問題:
Execution stacks take up memory: 資源有限
Context switching is not free:占用額外資源
Node的解決這個問題思路:
在網絡應用中,比較慢的環節主要在 磁盤讀取 或 網絡請求階段,這時候CPU處于閑置狀態。
如果在等待I/O操作時,能夠釋放處于閑置狀態的CPU,則可以很大程度上利用資源;
那么接下來的問題就在于,如何在I/O 操作完成后,繼續執行后續的操作;
解決方案:事件驅動,采用回調。這和瀏覽器中的Event Loop是一樣的道理。
市場上已經有一些使用同樣處理思路的框架。
EventMachine:處理網絡請求的框架,主要是面向ruby;
Twisted:用Python實現的基于事件驅動的網絡引擎框架;
具體是怎么做的?
在程序運行時,V8 會把I/O操作等耗時較多操作及相關回調一并交給libuv去處理。而V8繼續執行后面的代碼。等到I/O操作完成后,libuv會將回調方法放到事件隊列中。
const fs = require("fs"); const readFile = (file) => { fs.readFile(file, (err, data) => { if(!err) console.log(data); }); } console.log("program start ......"); readFile("file.json"); console.log("readFile has put the I/O "); console.log("program end!!!!!");
負責異步程序調度的工作就是libuv做的事情。
Libuv is a multi-platform support library with a focus on asynchronous I/O.
在上述程序中,當遇到文件讀取操作時,V8會把js接口轉成C++接口 同時把回調方法一并交給libvu。此時libuv接管這個文件讀取任務。當文件讀取完成后,libuv就會把這個事件的回調函數扔到事件隊列里。當下一次檢查事件隊列時,就會執行該回調函數。
question: 既然這樣,怎么理解node中的單線程?
再捋一捋Node, V8 和libuv的關系。
1) Node主要由V8 javascript引擎和libuv組成;
2) v8引擎主要負責解釋執行js代碼,碰到需要異步的操作會交給libuv處理;
3) libuv本身是獨立的c語言庫,可以直接使用c/c++來調用;
在瀏覽器端,JS是沒有能力進行文件讀取操作的。js最初的能力也就限制在表單校驗上。而在Node中,JS可以操作本地文件,建立網絡連接。這肯定是Node干的好事!
再來說說Node中其它一些組成部分
Node擴展了JS的能力:builtin modulesbuiltin modules是由C++代碼寫成各類模塊,包含了crypto,zlib, file, net等基礎功能。
native modules除了builtin modules, 還有一個native modules。它們是用js編寫的內建模塊,提供給程序開發者使用。
fs
http
builtin modules 和 native modules都屬于核心模塊。核心模塊在Node源碼編譯的過程中,編譯進了二進制文件。所以在Node進程啟動的時候,部分核心模塊就已經被直接加載到了內存當中。
至此,Node的基本構成和運行原理已經講完了。
補充一句:Node.js的單線程并不是真正的單線程,只是開啟了單個線程(可以定義為主線程)進行業務處理,同時開啟了其他線程專門處理I/O。當一個指令到達主線程,主線程發現有I/O之后,直接把這個事件傳給libuv處理。libuv會管理一個線程池,I/O操作就是由線程池里面的線程完成的。等I/O 操作完成,libuv,會把對應I/O操作的回調放到事件循環當中。在線程上,不會等待I/O 操作完成,繼續執行后續的代碼。這就是“單線程”、“異步I/O”。
IO.js
var fork = require("child_process").fork; var fs = require("fs"); console.log("start......"); // blocking // console.log(fs.readFileSync("test.json", "utf-8")); // non blocking var childProcess = fork("another-thread.js"); childProcess.on("message", function(data){ console.log(data); }) console.log("end !!!");
another-child.js
var fs = require("fs"); process.send( fs.readFileSync("test.json", "utf-8"));
Everything runs in parallel except your code! 在Node中)除了代碼,一切都是并行的!
由于node中主任務的執行是以單線程的方式進行,如果程序出錯導致崩潰,就會終止整個流程。為此,市場上有些Node進程管理工具,它們會維護Node程序的狀態,當程序掛掉時,會自動重啟。比如我們使用的pm2。
NPM對于node沒有的一些模塊(native modules),可以引入外部模塊。這些外部模塊通常是其它開發者貢獻的。
那么問題來了,對于數量眾多的模塊中,如何快速找到自己想要的并能夠快速的引進到自己的項目當中。
這就是npm幫我們做的工作。
Use npm to install, share, and distribute code; manage dependencies in your projects; and share & receive feedback with others.
npm官網
模塊安裝:
模塊共享
發布代碼
管理依賴
共享和反饋
NPM的基本模式NPM是JavaScript包的管理器。
廣義的npm的構成npm consists of three distinct components:
the website: NPM官方站點
the Command Line Interface (CLI) : NPM命令行工具
the registry: JS模塊的數據庫
Use the website to discover packages, set up profiles, and manage other aspects of your npm experience. For example, you can set up Orgs (organizations) to manage access to public or private packages.
The CLI runs from a terminal. This is how most developers interact with npm.
The registry is a large public database of JavaScript software and the meta-information surrounding it.
npm中的使用npm默認隨node一起安裝,在Node安裝完成后,npm已經安裝。
查找一個包去npm官網,按關鍵詞查找。
管理模塊npm install [module name] :普通安裝方式,包安裝完成后,會在當前目錄生成一個node_modules目錄。這是一個存放外部js模塊的地方,通過npm安裝的包都放在node_modules下。
npm install -g [module name]:全局安裝,模塊被安裝在node安裝路徑下的 node_modules中。
npm install [folder path]:可以指定npm 安裝某個目錄folder path下的的文件,前提是這個目錄下包含package.json文件。
npm install [module name]@[version]: 安裝包的時候,指定對應的版本號。
npm isntall chalk@latest : 安裝最新版
npm install chalk@2.0.0: 安裝2.0.0版
npm install chalk@">=2.0.0":安裝大于2.0.0的版本
npm install --save-prod [module name]: 在本地安裝包,并將安裝信息寫入 package.json文件中的dependencies中, 不寫--save-prod 或者只寫 --save 默認跟 --save-prod一樣。
npm install --save-dev [module name]:在本地安裝包,并將安裝信息寫入 package.json文件中的devDependencies中
dependencies:在生產環境中需要用到的依賴
devDependencies:在開發、測試環境中用到的依賴
npm update [module name]: 更新本地模塊
npm uninstall [module name]: 卸載模塊
package.jsonpackage.json是一個node和npm都會自動讀取的配置文件,它里面是個標準的JSON格式字符串。
對于NPM而言, package.json做了以下工作:
存儲項目依賴的所有包
允許你指定項目依賴的包的版本規則,不滿足項目需求的版本,不需要
讓你的項目構建具有可復用性,容易分享你的項目
對于你的項目而言,package.json定義了一些基礎信息,比如項目名稱,版本等等。
pakcage.json必須具有的兩個字段: name 和 version。這倆個字段有什么意義呢?
NPM 作為一個包管理平臺,當有開發者提交(publish)模塊時,必須提供一些基本信息便于管理。
name: 項目名稱或者模塊名稱
version:版本號,應當遵循 x.x.x的格式
description:項目信息描述,方便別人了解你的模塊,也利于搜索
keywords:項目的關鍵詞,便于搜索
homepage:項目的主頁;
scripts:scripts屬性是一個對象,里面的每一個屬性對應一段腳本;腳本可以使用 npm run + 屬性名 執行
main:指定項目的程序入口文件,該文件的exports對象同時也是require項目時,取到的對象。
repository:指明代碼存在的地址,便于別人更好的查看你的源碼
dependencies:一個對象,配置模塊依賴的模塊列表,key是模塊名稱,value是版本描述(遵循semantic規則)
devDependencies:一個對象,開發或測試過程中的一些依賴模塊,跟上線后的依賴模塊區分出來
engines: 指定項目運行的Node版本
author:項目的開發者
contributors:一堆項目的開發者
{ "name": "vue-todo", "version": "1.0.0", "description": "a simply todolist using vuejs", "scripts": { "start": "node server.js", "stop": "egg-scripts stop --title=egg-server-example", "dev": "egg-bin dev" }, "dependencies": { // 線上生產環境必須,當然開發環境也會用到 "babel-runtime": "^6.23.0", "vue": "^2.0.1", "vue-localstorage": "^0.1.1", "vuex": "^2.2.1" }, "devDependencies": { // 開發環境會用到的東東 "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2", "webpack-merge": "^0.14.1" } }semantic versioning(語義化版本規則)
版本格式:主版本號.次版本號.修訂號, 例如 1.2.3
版本號遞增規則如下:
主版本號:當你做了不兼容的 API 修改; 1.2.3 ---> 2.0.0
次版本號:當你做了向下兼容的功能性新增; 1.2.3 ---> 1.3.0
修訂號:當你做了向下兼容的問題修正; 1.2.3 ---> 1.2.4
在package.json定義版本規則的時候,可以這么做:
如果只打算接受補丁版本的更新(也就是最后一位的改變),就可以這么寫:
1.0
1.0.x
~1.0.4
如果接受小版本的更新(第二位的改變),就可以這么寫:
1
1.x
^1.0.4
如果可以接受大版本的更新(自然接受小版本和補丁版本的改變),就可以這么寫:
*
x
在使用npm install --save || npm install --save-dev 安裝的時候,寫入pakcage.json中的依賴,默認接受小版本的更新,即在版本號前添加 "^"。
NPM document
Node 模塊Node中的模塊分為兩類:
核心模塊(Node中內嵌的,比如fs、http等)
文件模塊(開發者自己編寫的,NPM上的模塊都屬于開發者自定義的模塊)
文件模塊是在運行的時候,動態加載。需要進行路徑分析,文件定位 和 編譯執行。
Node加載模塊時,優先從緩存中加載,如果緩存中不存在該模塊,才會按照上述的三個步驟進行模塊加載。
模塊標識符在Node中,主要有以下幾類:
核心模塊,比如fs, http等
以. 或 ..開頭的相對路徑文件模塊
以/ 開頭的絕對路徑文件模塊
非路徑形式的文件模塊;
這幾類模塊的加載速度是依次降低的。
module.js
同瀏覽器中的window一樣,在Node中的全局變量都掛在global下。
先說一下,常用到一些變量:
__dirname: 當前模塊所在目錄
__filename: 當前模塊文件名稱
require: 引入其它模塊
module:
exports
上面5個變量,貌似全局變量,但不是全局變量。他們都是模塊系統下的東西。
question: 它們不在全局變量下,那它們為何可以在模塊中直接調用?
Node引入模塊在Node中,引入模塊分為三個步驟:
路徑分析
文件定位
編譯執行
模塊編譯編譯和執行是引入文件模塊的最后一個階段。
.js文件 : Node 會對js源碼進行一個首尾的封裝。返回一個function,并將當前的環境的exports,require,module,__dirname,__filename作為形參傳遞給這個function。
包裝后的代碼:
(function(exports, require, module, __filename, __dirname){ // js文件中的源碼 })
這就是為什么 它們不在 全局變量下,卻可以在模塊當中使用的原因。
.node文件: 對于.node文件, 實際上并不需要編譯過程。因為.node文件本身就是C/C++編譯后的文件,它只有加載和執行過程。
.json文件: Node 會讀取json文件內容,并將它賦予exports對象,直接傳遞給第三方調用。
在NPM中的模塊,基本屬于Node中文件模塊里的Javascript模塊。
require的規則Node的模塊系統參照CommonJS規范實現。
如果參數字符串不以./或/或../開頭,說明要加載的不是一個文件,而是一個默認提供的核心模塊。
此時則先在node平臺所提供的核心模塊當中找;
然后再尋找NPM模塊(即第三方模塊包,或自己寫的模塊包)
在尋找NPM模塊包時,會從當前目錄出發,向上搜索各級當中的node_modules文件夾當中的文件,但若有兩個同名文件,則遵循就近原則。(module.paths是一個模塊路徑數組。)
如果require當中的參數字符串以/開頭:則表示從系統根目錄開始尋找該模塊文件。
如果require當中的參數字符串以./(從當前目錄出發)或../(從上一級目錄出發)開頭:表示按照相對路徑,從當前文件所在的文件夾開始尋找要載入的模塊文件。
按js文件來執行(先找對應路徑當中的module.js文件來加載)
按json文件來解析(若上面的js文件找不到時,則找對應路徑當中的module.json文件來加載)
按照預編譯好的c++模塊來執行(尋找對應路徑當中的module.node文件來加載)
若參數字符串為一個目錄(文件夾)的路徑,則自動先查找該文件夾下的package.json文件,然后再再加載該文件當中main字段所指定的入口文件。(若package.json文件當中沒有main字段,或者根本沒有package.json文件,則再默認查找該文件夾下的index.js文件作為模塊來載入。)
processprocess 對象是一個全局變量,它提供當前 Node.js 進程的有關信息,以及控制當前 Node.js 進程。
process.argv: 包含命令行參數的數組。第一個元素會是"node",第二個元素將是.js文件的名稱,接下來的參數依次是命令行參數
process.execArgv: 啟動進程所需的 node 命令行參數。這些參數不會在 process.argv 里出現,并且不包含 node 執行文件的名字,或者任何在名字之后的參數。這些用來生成子進程,使之擁有和父進程有相同的參數
process.env: 獲取當前系統環境信息的對象,輸出內容是環境變量等內容,這個對象可以修改
nextTick(callback): 將callback放到事件輪詢隊列首位,下一次事件輪詢開始時,先執行callback
process.abort:結束當前進程
process.kill(pid):結束一個進程
process.js
console.log(process.argv); console.log(process.execArgv);
node process.js # [ "/usr/local/bin/node", "/root/node-demo/process.js" ] # [] node process.js abc 234 cvb=cvb # [ "/usr/local/bin/node", # "/root/node-demo/process.js", # "abc", # "234", # "cvb=cvb" ] # [] node --harmony --use-openssl-ca process.js abc 234 cvb=cvb # [ "/usr/local/bin/node", # "/root/node-demo/process.js", # "abc", # "234", # "cvb=cvb" ] # [ "--harmony", "--use-openssl-ca" ]
Node"Process document
fsfile.js
Node"FileSystem document
questions require("../fs/file.js")這里是異步還是同步?
deno,下一代Node?package.json
node_modules
gyp
等等............
Node之父JS大會介紹deno時的PPT(2018)
Node之父JS大會介紹Node時的PPT(2009)
Nodejs的運行原理-科普篇
視頻:callstack 和異步 基本原理
Awesome Micro npm Packages
libuv 官網
[深入淺出Node.js]()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98694.html
摘要:類鏈表容器也是通過對比源碼進行對比學習。增加一個結點不帶,直接尾插法當鏈表里沒有一個元素時,頭尾都是該結點,并且該結點的前后都是空的。尾結點是該結點的前驅結點,該結點是尾節點的后繼結點,更新尾節點。 LinkedList類 鏈表容器也是通過對比jdk源碼進行對比學習。 1.定義結點類型 class Node{ E item; Node next; Node prev; Node(No...
摘要:官方文檔官方文檔,官方文檔永遠是學習資料的第一步起步扎實的基本功。學習的新特性,理解,建議可以看看阮一峰的教程。的學習曲線會比較長,需要了解到的常用命令,以及和的模塊規范,的也很多,其實更多的是屬于一項后端語言。 學習Vue2.0的建議順序 注:本文是看過其他關于vue文章之后的想法,歡迎轉載,請注明出處。 Vue官方文檔:Vue2.0官方文檔,官方文檔永遠是學習資料的第一步 起步...
摘要:學習手動寫一個簡單的進行理解結點的定義的基礎時一個數組,數組里每個元素是一個,他必須包括值,鍵值,,下一個結點,同一個值的結點用一條鏈栓起來。第一個結點的特殊操作第一個對上了修改一個元素查找一個元素 HashMap學習--手動寫一個簡單的HashMap進行理解 1.結點Node的定義 public class Node { public int hash; public...
摘要:之前寫過一篇天學通前端開發,內容主要講的就是前端學習路徑,今天再來寫一篇零基礎的學習路徑,希望能幫編程零基礎的前端愛好者指明方向。十框架三選一,零基礎的初學者強烈推薦,如果是后臺轉前端推薦,如果技術型前端,推薦。 之前寫過一篇26天學通前端開發,內容主要講的就是前端學習路徑,今天再來寫一篇零基礎的JavaScript學習路徑,希望能幫編程零基礎的前端愛好者指明方向。 一、開發環境和Ja...
摘要:由于這種特性,某一個任務的后續操作,往往采用回調函數的形式進行定義。另外,回調函數本身的第一個參數,約定為上一步傳入的錯誤對象。這種寫法有一個很大的好處,就是說只要判斷回調函數的第一個參數,就知道有沒有出錯,如果不是,就肯定出錯了。 REPL環境 在命令行鍵入node命令,后面沒有文件名,就進入一個Node.js的REPL環境(Read–eval–print loop,讀取-求值-輸出...
摘要:謹記,請勿犯這樣的錯誤。由于在之前的教程中,積累了堅實的基礎。其實,這是有緣由的其復雜度在早期的學習過程中,將會帶來災難性的影響。該如何應對對于來說,雖然有大量的學習計劃需要采取,且有大量的東西需要學習。 前言倘若你正在建造一間房子,那么為了能快點完成,你是否會跳過建造過程中的部分步驟?如在具體建設前先鋪設好部分石頭?或直接在一塊裸露的土地上先建立起墻面? 又假如你是在堆砌一個結婚蛋糕...
閱讀 3960·2021-11-24 09:38
閱讀 1225·2021-10-19 11:42
閱讀 1829·2021-10-14 09:42
閱讀 2154·2019-08-30 15:44
閱讀 544·2019-08-30 14:04
閱讀 2889·2019-08-30 13:13
閱讀 1949·2019-08-30 12:51
閱讀 956·2019-08-30 11:22