成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Node_深入淺出Node

shinezejian / 1518人閱讀

摘要:簡介項(xiàng)目命名為就是一個(gè)服務(wù)器單純開發(fā)一個(gè)服務(wù)器的想法,變成構(gòu)建網(wǎng)絡(luò)應(yīng)用的一個(gè)基本框架發(fā)展為一個(gè)強(qiáng)制不共享任何資源的單線程,單進(jìn)程系統(tǒng)。單線程弱點(diǎn)無法利用多核錯(cuò)誤會(huì)引起整個(gè)應(yīng)用退出,應(yīng)用的健壯性大量計(jì)算占用導(dǎo)致無法繼續(xù)調(diào)用異步。

NodeJs簡介

Ryan Dahl項(xiàng)目命名為:web.js 就是一個(gè)Web服務(wù)器.
單純開發(fā)一個(gè)Web服務(wù)器的想法,變成構(gòu)建網(wǎng)絡(luò)應(yīng)用的一個(gè)基本框架.
Node發(fā)展為一個(gè)強(qiáng)制不共享任何資源的單線程,單進(jìn)程系統(tǒng)。
每一個(gè)Node進(jìn)程都構(gòu)成這個(gè)網(wǎng)絡(luò)應(yīng)用中的一個(gè)節(jié)點(diǎn),這是它名字所含意義的真諦。

Node的誕生歷程

2009年3月,Ryan Dahl在博客宣布并創(chuàng)建

2009年5月,在GitHub上發(fā)布最初的版本

2009年12月和2010年4月,兩屆JSConf大會(huì)安排了Node的講座

2010年底,Ryan Dahl加入Joyent全職負(fù)責(zé)Node的發(fā)展

2011年7月,發(fā)布Windows版本

2011年11月,成為GitHub上面關(guān)注度最高的項(xiàng)目

2012年1月底,Ryan Dahl 將掌門人身份交給NPM的作者Issac Z.Schlueter

2013年7月,發(fā)布穩(wěn)定版V0.10.13

隨后,Node的發(fā)布計(jì)劃主要集中在性能上面,V0.14后正式發(fā)布V1.0版本

選擇JavaScript

高性能(chrome的V8引擎的高性能)

符合事件驅(qū)動(dòng)(JavaScript在瀏覽器中有廣泛的事件驅(qū)動(dòng)方面的應(yīng)用)

沒有歷史包袱(為其導(dǎo)入非阻塞的I/O庫沒有而外阻力)

Node給JS帶來的意義

Node結(jié)構(gòu)與Chrome十分相似,基于事件驅(qū)動(dòng)的異步架構(gòu)

Node中JS可以訪問本地文件,搭建服務(wù)器,連接數(shù)據(jù)庫

Node打破了過去JS只能在瀏覽器中運(yùn)行的局面,前后端編程環(huán)境統(tǒng)一

Node特點(diǎn)

異步I/O
事件與回調(diào)函數(shù)
單線程

* child_progress:解決單線程中大量算量的問題
* Master-Worker:管理各個(gè)工作進(jìn)程

跨平臺(tái):兼容Windows和*nix平臺(tái)

構(gòu)建異步I/O,從文件讀取到網(wǎng)絡(luò)請(qǐng)求。
可以從語言層面很自然的進(jìn)行并行I/O 操作。每個(gè)調(diào)用之間無序等待之前I/O調(diào)用結(jié)束。

事件編程方式:輕量級(jí),松耦合,只會(huì)關(guān)注事務(wù)點(diǎn)。

單線程弱點(diǎn):

無法利用多核CPU

錯(cuò)誤會(huì)引起整個(gè)應(yīng)用退出,應(yīng)用的健壯性

大量計(jì)算占用CPU導(dǎo)致無法繼續(xù)調(diào)用異步I/O。

瀏覽器中JavaScript與UI公用一個(gè)線程,JavaScript長時(shí)間執(zhí)行會(huì)導(dǎo)致UI的渲染和響應(yīng)被中斷。
在Node中,長時(shí)間占用CPU到孩子后續(xù)的異步I/O發(fā)不出調(diào)用,已經(jīng)完成的異步I/O的回調(diào)函數(shù)也會(huì)得不到執(zhí)行。

解決:
child_progress:解決單線程中大量算量的問題
Master-Worker:管理各個(gè)工作進(jìn)程 (管理子進(jìn)程)

啟用一個(gè)完全獨(dú)立的進(jìn)程,將需要計(jì)算的程序發(fā)送給進(jìn)程。通過時(shí)間將結(jié)果傳遞回來。(消息傳遞的方式來傳遞運(yùn)行結(jié)果)

采用消息傳遞的方式: 保持應(yīng)用模型的簡單和低依賴。

Node的應(yīng)用場(chǎng)景

I/O密集型

面向網(wǎng)絡(luò)且擅長并行I/O,能夠有效的組織起更多的硬件資源。
利用事件循環(huán)的處理機(jī)制,資源占用極少。

不是很擅長CPU密集型業(yè)務(wù),但是可以合理調(diào)度

通過編寫C/C++擴(kuò)展的方式更高效的利用CPU

與遺留系統(tǒng)問題和平共處

LinkeDin, 雪球財(cái)經(jīng) 

分布式應(yīng)用

阿里的數(shù)據(jù)平臺(tái),對(duì)Node的分布式應(yīng)用
分布式應(yīng)用要求:對(duì)可伸縮性要求高。
具體應(yīng)用:
中間層應(yīng)用NodeFox,ITer,將數(shù)據(jù)庫集群做了劃分和映射,查詢調(diào)用一句是針對(duì)單張表進(jìn)行SQL查詢,中間層分解查詢SQL,并行的去多態(tài)數(shù)據(jù)庫中獲取數(shù)據(jù)并合并。
NodeFox作用:實(shí)現(xiàn)對(duì)多臺(tái)MySQL數(shù)據(jù)的查詢
ITer作用:查詢多個(gè)數(shù)據(jù)庫(指的是不同數(shù)據(jù)庫,MySQL,Oracle等)            

Node的使用者

前后端編程語言環(huán)境統(tǒng)一:雅虎開放了Cocktail框架

Node帶來的高性能的I/O用于實(shí)時(shí)應(yīng)用:Voxer和騰訊

Voxer:實(shí)時(shí)語音
騰訊:Node應(yīng)用在長連接,實(shí)時(shí)功能

并行I/O使得使用者可以更高效地利用分布式環(huán)境:阿里巴巴和eBay

利用Node并行I/O的能力,更高校的使用已有的數(shù)據(jù)

并行I/O,有效利用穩(wěn)定接口提升Web渲染能力:雪球財(cái)經(jīng)和LinkedIn

云計(jì)算平臺(tái)提供Node支持

游戲開發(fā)領(lǐng)域:網(wǎng)易的pomelo實(shí)時(shí)框架

工具類應(yīng)用

模塊機(jī)制

Node的模塊機(jī)制
模塊在引用過程中的編譯,加載規(guī)則
CommonJs規(guī)范為JavaScript提供了一個(gè)良好基礎(chǔ),JavaScript能夠在任何地方運(yùn)行。

CommonJS規(guī)范

規(guī)范涵蓋:模塊,二進(jìn)制,Buffer,字符集編碼,I/O流,進(jìn)程環(huán)境,文件系統(tǒng),套接字,單元測(cè)試,Web服務(wù)器網(wǎng)管接口,包管理

JavaScript規(guī)范缺陷

沒有模塊系統(tǒng)

標(biāo)準(zhǔn)庫較少

沒有標(biāo)準(zhǔn)接口

缺乏包管理系統(tǒng)

Node借鑒CommonJS的Modules規(guī)范實(shí)現(xiàn)了一套非常易用的模塊系統(tǒng)
NPM對(duì)Packages規(guī)范的完好支持使得Node在應(yīng)用開發(fā)過程中更加規(guī)范

CommonJS的模塊規(guī)范

模塊應(yīng)用

var math = require("math");

在CommonJs規(guī)范中,存在require();方法,這個(gè)方法接收模塊標(biāo)識(shí),以此引入一個(gè)模塊的API到當(dāng)前上下文中。

模塊定義

require(): 引入外部模塊。
exports對(duì)象: 導(dǎo)出當(dāng)前的方法或者變量,而且是唯一導(dǎo)出的出口
module對(duì)象:表示自身模塊,而exports是module的屬性。

Node中,一個(gè)文件就是一個(gè)模塊,將方法掛載到exports對(duì)象作為屬性定義導(dǎo)出方式.

模塊標(biāo)識(shí)

傳遞給require() 方法的參數(shù),必須符合小駝峰命名的字符串,或者以 .,..開頭的相對(duì)路徑,或者絕對(duì)路徑。 js文件可以沒有后綴.js

模塊的意義:
將類聚的方法和變量等限定在私有的作用域中,同時(shí)支持引入和導(dǎo)出的功能順暢的連接上下游依賴。

Node模塊的實(shí)現(xiàn)

Node中引入模塊的步驟:
1:路徑分析
2:文件定位
3:編譯執(zhí)行

模塊分類

核心模塊:Node提供的模塊
文件模塊:用戶編寫的模塊

核心模塊在Node源代碼的編譯過程中,編譯進(jìn)了二進(jìn)制執(zhí)行文件。
Node進(jìn)程啟動(dòng)時(shí),部分核心模塊就直接被加載近內(nèi)存中。

文件模塊是在運(yùn)行時(shí)動(dòng)態(tài)加載,需要完成的路徑分析,文件定位,編譯執(zhí)行的過程。

模塊加載過程

優(yōu)先從緩存加載

Node對(duì)引入過的模塊都是進(jìn)行緩存,減少二次引入時(shí)的開銷。(Node緩存是編譯和執(zhí)行之后的對(duì)象)

不論是核心模塊還是文件模塊,require()方法對(duì)同模塊的二次加載都采用緩存優(yōu)先的方式。 不同之處,核心模塊的緩存檢查優(yōu)先于文件模塊的緩存檢查

路徑分析和文件定位

○ 模塊標(biāo)識(shí)符分析

核心模塊

路徑形式的文件模塊

自定義模塊

模塊標(biāo)識(shí)符分類:

核心模塊:http,fs,path 等

.,..開始的相對(duì)路徑的文件模塊

/開始的絕對(duì)路徑文件模塊

非路徑形式的文件模塊(自定義模塊)

核心模塊
核心模塊的優(yōu)先級(jí)僅此于緩存加載,在Node的源碼編譯過程中,已經(jīng)編譯為二進(jìn)制代碼,其加載過程最快。

如果想加載和核心模塊標(biāo)識(shí)相同的模塊,必須選擇修改標(biāo)識(shí)或換用路徑的方式。

路徑形式的文件模塊
.,..開始的相對(duì)路徑的文件模塊。require();方法會(huì)將路徑轉(zhuǎn)為真實(shí)路徑,并以真實(shí)路徑作為索引,將編譯執(zhí)行后的結(jié)果存放入緩存中。

文件模塊指明了確切的文件位置,在查找過程中節(jié)約大量時(shí)間,其加載速度慢于核心模塊。

自定義模塊
特殊的文件模塊,可能是一個(gè)文件或包的形式。這類模塊查找最費(fèi)時(shí),也是所有方式最慢的一種。

模塊路徑:Node在定位文件模塊的具體文件時(shí)定制的查找策略。表現(xiàn)為一個(gè)路徑組成的數(shù)組。

node_modules 會(huì)按照類似JavaScript的原型鏈查查找方式,在加載過程中,Node會(huì)租個(gè)嘗試模塊路徑中的路徑。直到找到文件為止。(文件路徑越深,模塊查找耗時(shí)越多)

○ 文件定位

文件擴(kuò)展名分析(.js,.node,.json次序補(bǔ)足擴(kuò)展名)

目錄分析和包

文件擴(kuò)展名分析
require(); 在分析標(biāo)識(shí)符的過程中,標(biāo)識(shí)符不含有文件擴(kuò)展名的情況,Node會(huì)按照 .js,.node,.json 補(bǔ)足擴(kuò)展名,以此嘗試

嘗試過程:需要調(diào)用fs模塊同步阻塞式判斷文件是否存在。
因?yàn)镹ode是單線程,這邊會(huì)引起性能問題。

避免方法:
方法1:如果是.node 和 .json文件,標(biāo)識(shí)符加上擴(kuò)展名
方法2:同步配合緩存,可以大幅度緩解Node中阻塞式調(diào)用的缺陷

目錄分析和包
在分析文件擴(kuò)展名之后,沒有找到對(duì)應(yīng)的文件,但卻得到一個(gè)目錄。當(dāng)作一個(gè)包來處理。

Node在當(dāng)前目錄下查找package.json(CommonJS包規(guī)范定義的包描述文件),通過JSON.parse();解析出包描述對(duì)象,從中取出main屬性指定的文件名進(jìn)行定位。如果文件名缺少擴(kuò)展名,將會(huì)進(jìn)去擴(kuò)展名分析的步驟。

如果main屬性指定的文件名錯(cuò)誤,或者更沒有package.json的文件,Node會(huì)將index當(dāng)作默認(rèn)文件名,然后一次查找index.js, index.node , index.json

如果在目錄分析的過程中沒有定位成功任何文件,則自定義模塊進(jìn)入下一個(gè)模塊路徑進(jìn)行查找。如果模塊路徑數(shù)組都遍歷完畢,依然沒有查找到目標(biāo)文件,則會(huì)拋出查找失敗的異常。

○ 編譯模塊

.js文件。通過fs模塊同步讀取文件后編譯執(zhí)行

.node文件

用C/C++編寫的擴(kuò)展文件
通過dlopen()方法加載最后編譯生成的文件

.json文件

通過fs模塊同步讀取文件后,
用JSON.parse()解析返回結(jié)果

其它擴(kuò)展名文件。它們被當(dāng)作.js文件載入

在Node中,每個(gè)文件模塊都是一個(gè)對(duì)象。
每一個(gè)編譯成功的模塊都會(huì)將其目錄作為索引緩存在Module._cache對(duì)象上.

JavaScript模塊的編譯
在編譯過程中,Node對(duì)獲取的JavaScript文件內(nèi)容進(jìn)行了頭尾包裝。

(function ( exprots, require, moduel, __filename, __dirname ) {  });

包裝之后,對(duì)每個(gè)文件之間進(jìn)行了作用域隔離,包裝之后的代碼會(huì)通過vm原生模塊的runIntThisContext();方法執(zhí)行 (類似eavl,只是具有明確上下文,不污染全局). 返回一個(gè)具體的function 對(duì)象。 最后,將當(dāng)前模塊對(duì)象的exports屬性,require()方法,module[模塊對(duì)象自身],以及在文件定位中得到的完整文件路徑和文件目錄作為參數(shù)傳遞給這個(gè)funciton() 執(zhí)行。

在執(zhí)行之后,模塊中的exports 屬性被返回給了調(diào)用方。exports屬性上的任何方法和屬性在外界都可以被調(diào)用得到。

有了exports的情況下,為何還存在module.exports.
給exports重新賦值,exports對(duì)象是通過形參的方式傳入的,直接賦值會(huì)改變形參的引用,但是不能改變作用域外的值

核心模塊

Node的核心模塊在編譯成可執(zhí)行文件的過程中編譯近了二進(jìn)制文件。

核心模塊:
C/C++編寫,存放在Node項(xiàng)目中src目錄下
JavaScript編寫,存放在lib目錄下

JavaScript核心模塊的編譯過程

轉(zhuǎn)存為C/C++代碼

編譯為JavaScript核心模塊

轉(zhuǎn)存為C/C++代碼
Node采用V8的js2c.py工具,將所有內(nèi)置的JavaScript代碼("src/node.js 和 lib/*.js") 轉(zhuǎn)換成C++里的數(shù)組,生成node_natives.h頭文件

啟動(dòng)Node進(jìn)程時(shí),JavaScript代碼直接加在進(jìn)內(nèi)存中。在加載過程中,JavaScript核心模塊經(jīng)歷標(biāo)識(shí)符分析后直接定位到內(nèi)存中,比普通的文件模塊從磁盤從查找快很多。

編譯為JavaScript核心模塊
JavaScript核心模塊與文件模塊區(qū)別:
獲取源代碼的方式(核心模塊是從內(nèi)存中加載的)以及緩存執(zhí)行結(jié)果的位置。

源文件通過process.bingding("natives"); 取出,編譯成功的模塊緩存到NativeModuel._cache對(duì)象上。 文件模塊緩存到Module._cache

function NativeModule(id) {
    this.filename = id + ".js";
    this.id = id;
    this.exports = {};
    this.loaded = false;
}
NativeModule._source = process.binding("natives");
NativeModule._cache = {};

C/C++核心模塊的編譯過程
核心模塊中,有些模塊全部有C/C++編寫,有些模塊則由C/C++完成核心部分,其它部分由JavaScript實(shí)現(xiàn)包裝或向外到處。

內(nèi)建模塊: 純C/C++編寫的部分
JavaScript主外實(shí)現(xiàn)封裝的模式是Noe能夠提高性能的常見方式.
Node的buffer,crypto,evals,fs,os等模塊都是部分通過C/C++編寫 (不直接被用戶調(diào)用)

內(nèi)建模塊的組織形式
每一個(gè)內(nèi)建模塊定義之后,都通過NODE_MODULE宏將模塊定義到node命名空間中。

內(nèi)建模塊的導(dǎo)出
在Node的所有模塊類型中,存在依賴關(guān)系。

一般的,不推薦文件模塊直接調(diào)用內(nèi)建模塊。如需調(diào)用,直接調(diào)用核心模塊。
因?yàn)?核心模塊中基本都封裝了內(nèi)建模塊。

Node在啟動(dòng)時(shí),會(huì)生成一個(gè)全局變量process,并提供Binding()方法謝祖加載內(nèi)建模塊。
轉(zhuǎn)為C/C++數(shù)組存儲(chǔ),通過process.binding("natives"); 取出防止NativeModule.source中.

在加載內(nèi)建模塊時(shí),先創(chuàng)建exports空對(duì)象,然后調(diào)用get_builtin_module() 方法取出內(nèi)建模塊對(duì)象,通過執(zhí)行resister_func()填充exports 對(duì)象,最后將exports對(duì)象安模塊名緩存,并返回給調(diào)用方完成導(dǎo)出。

核心模塊的引入流程
1.NODE_MODULE(node_os,reg_func)
2.get_builtin_module("node_os")
3.process.binding("os")
4.NativeModule.require("os")
5.require("os")

編寫核心模塊

前提條件:

GYP項(xiàng)目生成工具

V8引擎C++庫

libuv庫

Node內(nèi)部庫

其他庫,zlib、openssl、http_parser等

C/C++擴(kuò)展模塊的編寫
C/C++擴(kuò)展模塊的編譯
C/C++擴(kuò)展模塊的加載

C/C++ 內(nèi)建模塊屬于最底層的模塊,屬于核心模塊,主要提供API給JavaScript核心模塊和第三方JavaScript文件模塊調(diào)用。

JavaScript核心模塊作用:
1: 作為C/C++內(nèi)建模塊的封裝層和橋接層,供文件模塊調(diào)用
2: 純粹的功能模塊,不需要跟底層交流,但有很重要。

文件模塊通常由第三方編寫,包括普通JavaScript模塊和C/C++擴(kuò)展模塊,主要調(diào)用方向?yàn)槠胀↗avaScript模塊調(diào)用擴(kuò)展模塊。

包與NPM

包結(jié)構(gòu)
package.json:包描述文件
bin:用于存放可執(zhí)行二進(jìn)制文件的目錄
lib:用于存放JavaScript代碼的目錄
doc:用于存放文檔的目錄
test:用于存放單元測(cè)試用例的代碼

包描述文件與NPM
必需字段:name,description,version,keywords,maintainers
必需字段:contributios,bugs,licences,repositories,dependencies

dependencies: 使用當(dāng)前包所依賴的包列表,NPM會(huì)通過這屬性幫助自動(dòng)加載依賴的包

NPM常用功能
查看幫助: npm -v

安裝依賴包
最常見: npm install express
全局安裝:npm install express -g
從本地安裝:npm install
從非官方源安裝:npm install underscore --registry=http://registry.url

NPM鉤子命令
package.json中的script字段:讓包在安轉(zhuǎn)或者卸載過程中提供鉤子機(jī)制

"scripts": {
    "preinstall": "preinstall.js",
    "install": "install.js",
    "uninstall": "uninstall.js",
    "test": "test.js"
}

在執(zhí)行npm install 時(shí), preinstall指向的腳本將會(huì)被加載執(zhí)行,然后install執(zhí)行的腳本會(huì)被執(zhí)行。在執(zhí)行npm install 時(shí),unstall指向的腳本也許會(huì)做一些清理工作。

NPM潛在問題
NPM平臺(tái)上面包質(zhì)量良莠不齊
Node代碼可以運(yùn)行在服務(wù)端,需要考慮安全問題

前后端共用模塊

模塊的側(cè)重點(diǎn)
Node的模塊引入過程,幾乎全部都是同步。

AMD規(guī)范
AMD規(guī)范:是CommonJS模塊規(guī)范的一個(gè)延伸

//通過數(shù)組引入依賴 ,回調(diào)函數(shù)通過形參傳入依賴
define(["someModule1", ‘someModule2’], function (someModule1, someModule2) {

    function foo () {
        /// someing
        someModule1.test();
    }

    return {foo: foo}
});

CMD規(guī)范
CMD規(guī)范:玉伯提出,區(qū)別定義模塊和依賴引入

 //CMD
define(function (requie, exports, module) {
    
    //依賴 就近書寫
    var a = require("./a");
    a.test();
    
    //軟依賴
    if (status) {
    
        var b = requie("./b");
        b.test();
    }
});

1.對(duì)于依賴的模塊AMD是提前執(zhí)行,CMD是延遲執(zhí)行。RequireJS從2.0開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不通過)。

2.CMD推崇依賴就近,AMD推崇依賴前置。

UMD規(guī)范
兼容多種模塊規(guī)范(Universal Module Definition)
UMD判斷是否支持Node.js的模塊(exports)是否存在且module不為undefiend,存在則使用Node.js模塊模式。
判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。

(function (root, factory) {

    if (typeof define === "function" && define.amd) {

        // AMD.
        define(["exports"], factory);

    } else if (typeof exports === "object" && typeof exports.nodeName !== "string") {

        // CommonJS
        factory(exports, require("echarts"));

    } else if ( typeof module !== "undefined" ** module.exports ) {

        // 普通Node模塊   
        module.exports = factory();

    } else {
        
        // Browser globals
        factory({}, root);
        
    }
    
}(this, function (exports) {

    //module ...

}));
異步I/O

事件循環(huán)是異步實(shí)現(xiàn)的核心,與瀏覽器中的執(zhí)行模型基本保持一致。

古老的Rhino,是較早服務(wù)器上運(yùn)行JavaScript,但是執(zhí)行模型并不像瀏覽器采用事件驅(qū)動(dòng).而是使用其它語言一樣采用同步I/O作為主要模型.

為什么要異步I/O

用戶體驗(yàn),消耗時(shí)間為max(M,N)

資源分配,讓單線程遠(yuǎn)離阻塞,更好利用CPU

用戶體驗(yàn)

異步概念: 瀏覽器中JavaScript在單線程上執(zhí)行,還與UI渲染共用一個(gè)線程。 表示JavaScript在執(zhí)行的時(shí)候UI渲染和響應(yīng)是處于停止?fàn)顟B(tài)

資源分配

單線程同步會(huì)因?yàn)樽枞鸌/O導(dǎo)致硬件資源的不到更優(yōu)使用。多線程編程中的死鎖,狀態(tài)同步等問題。

選擇: 利用單線程,原理多線程死鎖,狀態(tài)同步等問題,利用異步I/O,讓單線程原理阻塞,更好的使用CPU。

異步I/O實(shí)現(xiàn)現(xiàn)狀

操作系統(tǒng)內(nèi)核對(duì)于I/O只有兩種方式:阻塞與非阻塞。
在調(diào)用阻塞I/O時(shí),應(yīng)用程序需要等待I/O才會(huì)返回結(jié)果.

阻塞I/O特點(diǎn):調(diào)用之后一定要等到系統(tǒng)內(nèi)核層面完成所有操作后,調(diào)用才結(jié)束。

異步I/O與非阻塞I/O
操作系統(tǒng)對(duì)計(jì)算機(jī)進(jìn)行了抽象,將所有的輸入輸出設(shè)備抽象為文件。內(nèi)核在進(jìn)行文件I/O操作時(shí),通過文件描述符進(jìn)行管理。
應(yīng)用程序如果需要進(jìn)行I/O調(diào)用,需要先打開文件描述符,然后再更具文件描述符去實(shí)現(xiàn)文件的數(shù)據(jù)讀寫。
阻塞I/O: 完成整個(gè)獲取數(shù)據(jù)的過程。
非阻塞I/O: 不帶數(shù)據(jù)直接返回,要獲取數(shù)據(jù),還需要通過文件描述符再次讀取。

為了獲取完整數(shù)據(jù),應(yīng)用程序需要重復(fù)調(diào)用I/O操作來確認(rèn)是否完成。這種重復(fù)調(diào)用判斷操作是否完成的叫做輪詢;

輪詢: CPU決策如何提供周邊設(shè)備服務(wù)的方式。 又稱為 “程控輸出入” (Programmed I/O)。
輪詢法: 由CPU定時(shí)發(fā)出詢問,依次詢問每一個(gè)周邊設(shè)備是否需要其服務(wù),有即給予服務(wù)。服務(wù)結(jié)束再詢問下一個(gè)周邊,重復(fù)詢問。

非阻塞I/O缺點(diǎn):輪詢?nèi)ゴ_認(rèn)是否完全完成數(shù)據(jù)獲取,會(huì)讓CPU處理狀態(tài)判斷,是對(duì)CPU資源的浪費(fèi)。需要減小I/O狀態(tài)判斷的CPU損耗.

輪詢技術(shù)

read

select

poll

epoll

kqueue

read : 重復(fù)調(diào)用來檢查I/O的狀態(tài)來完成完整數(shù)據(jù)的讀取。
缺點(diǎn):性能最低

select : 文件描述符上的事件狀態(tài)來進(jìn)行判斷
缺點(diǎn):最多可同時(shí)檢查1024個(gè)文件描述符.

poll : 鏈表的方式避免數(shù)組長度的限制,能避免不需要的檢查
缺點(diǎn):文件描述符較多,性能低下。

epoll : I/O事件通知機(jī)制。
進(jìn)入輪詢的時(shí)候如果沒有檢測(cè)到I/O事件,將會(huì)進(jìn)行休眠,知道事件發(fā)生將它喚醒。
事件通知,執(zhí)行回調(diào)的方式。而不是遍歷查詢。
特點(diǎn):不會(huì)浪費(fèi)CPU,執(zhí)行效率較高。

kqueue : 實(shí)現(xiàn)方式和epoll類似,存在FreeBSD系統(tǒng)下。

輪詢?nèi)秉c(diǎn):
輪詢對(duì)于應(yīng)用程序,仍然是一種同步,應(yīng)用程序讓然需要等待I/O完全返回,依舊花費(fèi)很多時(shí)間來等待。等待期間CPU要么用于遍歷文件描述符的狀態(tài),要么用戶休眠等待事件發(fā)生。

理想的非阻塞異步I/O

應(yīng)用程序發(fā)起非阻塞調(diào)用,無需通過遍歷或者事件喚醒等方式輪詢,可以直接處理下一個(gè)任務(wù)。
在I/O完成后通過信號(hào)或回調(diào)將數(shù)據(jù)傳遞給應(yīng)用程序。

現(xiàn)實(shí)中的異步I/O

*nix平臺(tái)下采用libeio配合libev實(shí)現(xiàn)I/O部分
windows平臺(tái)采用IOCP是實(shí)現(xiàn)異步I/O

部分線程阻塞I/O 或者 非阻塞I/O + 輪詢技術(shù) -> 完成數(shù)據(jù)獲取。
一個(gè)線程計(jì)算處理
通過線程之間的通信將I/O得到的數(shù)據(jù)進(jìn)行傳遞。

IOCP: 調(diào)用異步方法,等待I/O完成之后的通知,執(zhí)行回調(diào),用戶無序考慮輪詢。(實(shí)現(xiàn)原理:線程池原理)

*nix將計(jì)算機(jī)抽象:磁盤文件,硬件,套接字,等幾乎所有計(jì)算機(jī)資源抽象為文件。

在Node中,無論是*nix還是Windows平臺(tái),內(nèi)部完成I/O任務(wù)的另有線程池。

Node的異步I/O

事件循環(huán)

觀察者

請(qǐng)求對(duì)象

執(zhí)行回調(diào)

事件循環(huán),觀察者請(qǐng)求對(duì)象,I/O線程池共同構(gòu)成Node異步I/O模型的基本要素。

事件循環(huán)
Node 自身的執(zhí)行模型 -- 事件循環(huán)。
在進(jìn)程啟動(dòng)時(shí),Node會(huì)穿件一個(gè)類似于while(true)的循環(huán),每執(zhí)行一次循環(huán)體的過程 稱之為: Tick(標(biāo)記,打勾) .每個(gè)Tick的過程就是查看是否有事件待處理,如果有,就取出事件及其相關(guān)的回調(diào)函數(shù)。如果存在關(guān)聯(lián)的回調(diào),就執(zhí)行它們。然后進(jìn)入下個(gè)循環(huán),如果不再有事件處理,就退出進(jìn)程。

觀察者

每個(gè)事件循環(huán)中有一個(gè)或者多個(gè)觀察者,而判斷是否有事件要處理的過程就是想這些觀察者詢問是否有要處理的事件。

瀏覽器類似事件觀察機(jī)制: 時(shí)間可能來自用戶的點(diǎn)擊或者加載某些文件時(shí)產(chǎn)生,而這些產(chǎn)生的事件都有對(duì)應(yīng)的觀察者。

事件循環(huán)是一個(gè)典型的生產(chǎn)者/消費(fèi)者模型,$watch $digest 機(jī)制。 異步I/O,網(wǎng)絡(luò)請(qǐng)求則是事件的生產(chǎn)者,遠(yuǎn)遠(yuǎn)不斷為Node提供不同類型的事件,這些事件被傳遞到對(duì)應(yīng)的觀察者那里,事件循環(huán)則從觀察則那里取出事件并處理。

請(qǐng)求對(duì)象

請(qǐng)求對(duì)象: 從JavaScript 發(fā)起調(diào)用到內(nèi)核執(zhí)行完I/O操作的過度過程中,存在一種中間產(chǎn)物.

請(qǐng)求對(duì)象是異步I/O過程中的重要中間產(chǎn)物,所有的狀態(tài)都保存在這對(duì)象中,包括送入線程池等待執(zhí)行以及I/O操作完畢后的回調(diào)處理。

Node中的異步I/O調(diào)用,回調(diào)函數(shù)不由開發(fā)者來調(diào)用。
從發(fā)出調(diào)用后,到回調(diào)函數(shù)被執(zhí)行,中間發(fā)生了什么?

fs.open() 示例:

fs.open = function ( path, flags, mode, cb ) {
    
    binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, cb);    

};

Node經(jīng)典調(diào)用方式:從JavaScript調(diào)用Node的核心模塊,核心模塊調(diào)用C++內(nèi)建模塊,內(nèi)建模塊通過libuv進(jìn)行系統(tǒng)調(diào)用。

調(diào)用的uv_fs_open() -> FsReqWrap (請(qǐng)求對(duì)象,作用:JavaScript層傳入的參數(shù)和當(dāng)前方法都封裝對(duì)象中)

回調(diào)函數(shù)被設(shè)置在FsReqWrap.oncomlete_sym 屬性上

req_wrap -> object_ -> Set(oncomlete_sym, callback);

包裝完之后,Windows調(diào)用:QueueUserWorkItem() 將FsReqWrap對(duì)象推入線程池中等待執(zhí)行

QueueUserWrokITem(&uv_fs_thread_proc, req, WT_EXTCUTEDEFAULT);

QueueUserWorkItem()
參數(shù)1:執(zhí)行的方法引用 fs_open() 的引用 uv_fs_thread_proc
參數(shù)2:uv_fs_thread_proc() 方法執(zhí)行時(shí)所需參數(shù)。
參數(shù)3:執(zhí)行的標(biāo)志。

調(diào)用完成之后,JavaScript調(diào)用立即返回,由JavaScript層面發(fā)起的異步調(diào)用的第一階段結(jié)束。
JavaScript線程可以繼續(xù)執(zhí)行當(dāng)前任務(wù)的后續(xù)操作。當(dāng)前的I/O操作在線程池中等待執(zhí)行,不管是否阻塞I/O,都不會(huì)影響到JavaScript線程的后續(xù)執(zhí)行。

執(zhí)行回調(diào)

回調(diào)通知,完成完整異步I/O的第二部分。

線程池中的I/O操作調(diào)用完畢后,會(huì)將結(jié)果存儲(chǔ)在req -> reslut 屬性上。然后調(diào)用 PostQueuedCompletionStatus(); 通知IOCP,告知當(dāng)前對(duì)象操作已經(jīng)完成:

PostQueuedCompletionStatus( (loop)->iocp, o, o, &(req)->overlappped )

PostQueuedCompletionStatus()作用: 向IOCP提交執(zhí)行狀態(tài),并將線程歸還給線程池。提交狀態(tài)
GetQueuedCompletionStatus() 作用: 提取

每個(gè)Tick的執(zhí)行中,會(huì)調(diào)用IOCP相關(guān)的GetQueuedCompletionStatus()方法檢查線程池中是否有執(zhí)行完的請(qǐng)求,如果存在,會(huì)將請(qǐng)求加入到I/O觀察者的隊(duì)列中,然后將其當(dāng)作事件處理。

I/O觀察者回調(diào)函數(shù)的行為: 取出請(qǐng)求對(duì)象的result屬性作為參數(shù),取出oncomlplete_sym屬性作為方法,然后調(diào)用執(zhí)行。以此達(dá)到調(diào)用JavaScript中傳入的回調(diào)函數(shù)的目的。

非I/O的異步API

定時(shí)器
定時(shí)器: setTimeout(),setInterval()
調(diào)用setTimeout()或者setInterval()創(chuàng)建的定時(shí)器會(huì)被插入到定時(shí)器觀察者內(nèi)部的一個(gè)紅黑樹(作用:實(shí)現(xiàn)關(guān)聯(lián)數(shù)組)中
每次Tick執(zhí)行時(shí),會(huì)從該紅黑樹中迭代取出定時(shí)器對(duì)象,檢查是否超過定時(shí)時(shí)間,如果超過,就形成一個(gè)事件,它的回調(diào)函數(shù)將立即執(zhí)行。

定時(shí)器問題:并非精確的(在誤差范圍內(nèi))。如果某一次循環(huán)占用的時(shí)間較多。那么下次循環(huán)時(shí),也許超時(shí)很久。
例如:setTImeout()設(shè)定一個(gè)任務(wù)在10毫秒后執(zhí)行,但是9毫秒后,有一個(gè)任務(wù)占用了5毫秒的CPU時(shí)間片,再次輪到定時(shí)器時(shí),時(shí)間就已經(jīng)過期4毫秒。

process.nextTick()

process.nextTick(): 操作比較輕量,高效

會(huì)將回調(diào)函數(shù)放入隊(duì)列中,在下一輪Tick時(shí)取出執(zhí)行,每輪循環(huán)中會(huì)將數(shù)組中的回調(diào)函數(shù)全部執(zhí)行完。

setImmediate()
setImmediate()與上者類似,但優(yōu)先級(jí)低于process.nextTick()
原因:事件循環(huán)對(duì)觀察者的檢查是有先后順序。 process.nextTick(); 屬于idle觀察者,setImmediate() 屬于check觀察者。
在每一輪循環(huán)檢查中,idle觀察者I/O觀察者,I/O觀察者先于check觀察者

會(huì)將結(jié)果保存在鏈表中。每輪循環(huán)中執(zhí)行鏈表中的一個(gè)回調(diào)函數(shù)。

process.nextTick(function () {
    console.log("nextTick延遲執(zhí)行");
});
setImmediate(function () {
    console.log("setImmediate延遲執(zhí)行");
});
console.log("正常執(zhí)行");

// 執(zhí)行結(jié)果
// 正常執(zhí)行
// nextTick延遲執(zhí)行
// setImmediate延遲執(zhí)行
事件驅(qū)動(dòng)和高性能服務(wù)器

事件驅(qū)動(dòng)的本質(zhì):通過主循環(huán)加事件觸發(fā)的方式來運(yùn)行程序。

經(jīng)典模型

同步式

一次只能處理一個(gè)請(qǐng)求,并且其余請(qǐng)求都處于等待狀態(tài)。

每進(jìn)程/每請(qǐng)求

每個(gè)請(qǐng)求啟動(dòng)一個(gè)進(jìn)程,這樣可以處理多個(gè)請(qǐng)求。
缺點(diǎn):不具備擴(kuò)展性。(因?yàn)橄到y(tǒng)資源有限)

每線程/每請(qǐng)求 (Apache)

為每個(gè)請(qǐng)求啟動(dòng)一個(gè)線程來處理。
優(yōu)點(diǎn):線程比進(jìn)程要輕量
缺點(diǎn):每個(gè)線程都占用一定內(nèi)存,當(dāng)大并發(fā)請(qǐng)求到來時(shí),內(nèi)存會(huì)很快耗光,導(dǎo)致服務(wù)器緩慢。

線程(程序執(zhí)行流的最小單元)

進(jìn)程(一段程序的執(zhí)行過程)
一個(gè)進(jìn)程中包括多個(gè)線程

異步編程 函數(shù)式編程

高階函數(shù)

高階函數(shù):把函數(shù)作為輸入或返回。作為參數(shù)或者返回值的函數(shù)

偏函數(shù)用法:創(chuàng)建一個(gè)調(diào)用另外一部分——參數(shù)或變量已經(jīng)預(yù)置的函數(shù)——的函數(shù)的用法。
通過指定部分參數(shù)來創(chuàng)建一個(gè)新的函數(shù)的形式。

異步編程的優(yōu)勢(shì)與難點(diǎn)

優(yōu)勢(shì)
優(yōu)勢(shì):基于事件驅(qū)動(dòng)的非阻塞I/O模型
效果:非阻塞I/O可以是CPU與I/O并不相互依賴等待,讓資源得到更好的利用。

難點(diǎn)

異常處理

異步I/O的實(shí)現(xiàn)主要包含兩個(gè)階段:提交請(qǐng)求和處理結(jié)果。
這兩個(gè)階段中間有事件循環(huán)的調(diào)度,兩者彼此不關(guān)聯(lián)。
異步方法則通常在第一階段提交請(qǐng)求后立即返回,因?yàn)楫惓2⒉话l(fā)生在這個(gè)階段。

函數(shù)嵌套過深

阻塞代碼

多線程編程

異步轉(zhuǎn)同步

異步編程解決方案

事件發(fā)布/訂閱模式
事件監(jiān)聽器模式:回調(diào)函數(shù)的事件化,又稱發(fā)布/訂閱模式 (鉤子(hook)機(jī)制)

Node中的很多對(duì)象大多具有黑盒的特點(diǎn),功能點(diǎn)較少。

事件發(fā)布/訂閱模式自身并無同步和異步調(diào)用的問題。但在Node中,emit()調(diào)用多半是伴隨著時(shí)間循環(huán)而異步觸發(fā)。

如果對(duì)一個(gè)時(shí)間添加了超過10個(gè)偵聽器,將會(huì)得到一條警告。(原因:偵聽器太多可能導(dǎo)致內(nèi)存泄漏)??梢酝ㄟ^ emitter.setMaxListeners(0); 去掉這個(gè)限制。
由于事件發(fā)布會(huì)引起一些列偵聽器執(zhí)行,如果事件相關(guān)的偵聽器過多,可能存在過多占用CPU
的情形。

繼承events模塊

util.inherits(constructor, superConstructor)
繼承原型對(duì)象上的方法。

var events = require("events");
var util = require("util");

function Stream () {
    events.EventEmitter.call(this);
}

util.inherits(Stream, events.EventEmitter);

利用事件隊(duì)列解決雪崩問題

事件訂閱/發(fā)布模式中,通常由一個(gè)once()方法。
作用:偵聽器只能執(zhí)行一次,在執(zhí)行之后就會(huì)將它與事件的關(guān)聯(lián)解除。
解決:過濾一些重復(fù)性的事件響應(yīng)。

問題:緩存中存放在內(nèi)存中,訪問速度十分快,用于加速數(shù)據(jù)訪問,讓絕大所數(shù)的請(qǐng)求不必重復(fù)去做一些抵消的數(shù)據(jù)讀取。
雪崩問題:高訪問量,大并發(fā)量的情況下緩存失效的情景,此時(shí)大量的請(qǐng)求同時(shí)涌入數(shù)據(jù)庫中,數(shù)據(jù)庫無法同時(shí)承受如此大的查詢請(qǐng)求。

解決方案:添加一個(gè)狀態(tài)鎖。

var proxy = new evnets.EventEmitter();
var status = "ready";
var select = function ( cb ) {
    proxy.once("selected", cb);
    if ( status === "ready" ) {
        status = "pending";
        db.select("SQL", function ( results ) {
            proxy.emit("selected", results);    
            status = "ready";
        });
    }
}
// 利用once() 方法,將所有請(qǐng)求的回調(diào)都?jí)喝胧录?duì)列中。保證回只會(huì)被執(zhí)行一次。

Gearman異步應(yīng)用框架中,利用noce()方法產(chǎn)生的效果。

多異步之間的協(xié)作方案

事件與偵聽器的關(guān)系是一對(duì)多,在異步編程中,會(huì)出現(xiàn)事件與偵聽器的關(guān)系多對(duì)一的情況。
一個(gè)業(yè)務(wù)邏輯可能依賴兩個(gè)通過回調(diào)或事件傳遞的結(jié)果。

問題:多個(gè)異步場(chǎng)景中的回調(diào)函數(shù)的執(zhí)行順序,且回調(diào)之間沒有任何交集。(多對(duì)一)
解決辦法:通過第三方函數(shù)和第三方變量來處理異步協(xié)作 -- 哨兵變量。

var after = function ( times, cb ) {
    
    var count = 0, resluts = {};
    
    return function ( key, value) {
        
        resluts[key] = value;
        
        count++;
        
        if ( coutn === times ) {
            
            cb(resluts);
            
        }
        
    }
    
}

哨兵變量和發(fā)布/訂閱模式完成多對(duì)多方案

// 哨兵變量
var after = function ( times, cb ) {
    
    var count = 0, resluts = {};
    
    return function ( key, value) {
        
        resluts[key] = value;
        
        count++;
        
        if ( coutn === times ) {
            
            cb(resluts);
            
        }
        
    }
    
}

// 哨兵變量和發(fā)布/訂閱模式完成多對(duì)多方案

var events = require("evnets");

var emitter = new events.Emitter();

var done = after(times, render);

emitter.on("done", done);
emitter.on("done", other);

fs.readFile(template_path, "utf-8", function ( err, template ) {
    
    emitter.emit("done", "template", template);
    
});

db.query(sql, function ( err, data ) {
    
    emitter.emit("done", "data", data);
    
});


l1on.get(function ( err, resources ) {
    
    emitter.emit("done", "resources", resources);
    
});

EventProxy模塊

all();來訂閱多個(gè)事件,當(dāng)每個(gè)時(shí)間都被觸發(fā)后,偵聽器才會(huì)執(zhí)行。
tail(); 偵聽器在滿足條件之后只會(huì)執(zhí)行一次。

場(chǎng)景:從一個(gè)接口多次讀取數(shù)據(jù),此時(shí)觸發(fā)的事件名或許是相同的。

利用after();方法實(shí)現(xiàn)時(shí)間在執(zhí)行多少次后執(zhí)行偵聽器的單一事件組合訂閱方式。

var proxy = new EventProxy();

proxy.after("data", 10, function ( datas ) {
    // TODO
});    

EventProxy原理

每個(gè)非all事件出發(fā)時(shí)都會(huì)觸發(fā)一次all事件。

EventProxy是將all當(dāng)作一個(gè)事件流的攔截層,在其中注入一些業(yè)務(wù)來處理單一時(shí)間無法解決的異步問題。

EventProxy的異常處理

exports.getCountent = function ( cb ) {
    
    var ep = new EventProxy();
    
    ep.all("tpl", "data", function ( tpl ,data ) {
        
        // 成功回調(diào)
        cb(null, {
            template: tpl,
            data: data
        });
        
    });
    
    // 綁定錯(cuò)誤處理函數(shù)
    ep.fail(cb);
    
    fs.readFile("template.tpl", "utf-8", ep.done("tpl"));
    
    db.get("some sql", ep.done("data"));
    
}

// EventProxy模塊提供fail()和done();實(shí)例方法來優(yōu)化異常處理。

Promise/Deferred模式

使用發(fā)布訂閱模式缺點(diǎn):使用事件的方式,執(zhí)行流程需要被預(yù)先設(shè)定。

場(chǎng)景:先執(zhí)行異步調(diào)用,延遲傳遞處理的方式。

Promises/A

Promises/A:對(duì)單個(gè)異步操作抽象定義

Promise 操作只會(huì)處在3中狀態(tài):未完成態(tài),完成態(tài)和失敗態(tài)。

Promise 的狀態(tài)只會(huì)出現(xiàn)從未完成態(tài)向完成態(tài)或失敗態(tài)轉(zhuǎn)化,不能逆反。完成態(tài)和失敗態(tài)不能互相轉(zhuǎn)化。
Promise的狀態(tài)一旦轉(zhuǎn)化,將不能被更改。

Promises/A要求Promise對(duì)象只需具備then()方法即可。

接受完成態(tài),錯(cuò)誤態(tài)的回調(diào)方法,在操作完成或出現(xiàn)錯(cuò)誤時(shí),將會(huì)調(diào)用對(duì)應(yīng)方法。

可選擇支持progress事件回調(diào)作為第三個(gè)方法。

then()方法之接受function對(duì)象,其余對(duì)象將被忽略。

then()方法繼續(xù)返回Promise對(duì)象,實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用。

then(fulfilledHandler, errorHandler, progressHandler);

Node的events模塊來完成Promise對(duì)象的then()方法

var util = require("util");
var events = require("events");

var Promise = function () {
    
    evennts.EventEmitter.call(this);
    
}

util.inherits(Promise, EventEmitter);

Promise.prototype.then = function ( fulfilledHandler, errorHandler, progressHandler ) {
    
    // 完成態(tài)
    if ( typeof fulfilledHandler === "function" ) {
        
        this.once("success", fulfilledHandler);
        
    }
    
    // 失敗態(tài)
    if ( typeof errorHandler === "function" ) {
        
        this.once("error", errorHandler);
        
    }
    
    
    // 執(zhí)行
    if ( typeof progressHandler === "function" ) {
        
        this.on("progress", progressHandler);
        
    }
    return this;
}

Deferred 延遲對(duì)象:觸發(fā) Promise對(duì)象中滿足條件的函數(shù),并實(shí)現(xiàn)這些功能。

// Deferred
var Deferred = function () {
    
    this.state = "unfulfilled";
    this.promise = new Promise();
    
}

// 實(shí)現(xiàn)完成態(tài)
Deferred.prototype.resolve = function ( obj ) {
    
    this.state = "fulfilled";
    this.promise.emit("success", obj);
    
}

// 實(shí)現(xiàn)失敗態(tài)
Deferred.prototype.reject = function ( err ) {
    
    this.state = "failed";
    this.promise.emit("error", err);
    
}

Deferred.prototype.progress = function ( data ) {
    
    this.promise.emit("progress", data);
    
}

Deferred主要用于內(nèi)部,用于維護(hù)異步模型的狀態(tài)
Promise作用于外部,通過then()方法暴露給外部以添加自定義邏輯。
Promise/Deferred -- 響應(yīng)對(duì)象

高級(jí)接口:不容易變化,不再有低級(jí)接口的靈活性。
低級(jí)接口:可以構(gòu)成更多更負(fù)責(zé)的場(chǎng)景。

Q模塊是Promises/A規(guī)范的一個(gè)實(shí)現(xiàn)

defer.prototype.makeNodeResolver = function () {
    
    var self = this;
    
    return function ( error, value ) {
        
        if ( error ) {
            
            self.reject(error);
            
        } else if ( arguments.length > 2 ) {
            
            self.resolve(array_slice(arguments,1));
            
        } else {
            
            self.resolve(value);
            
        }
        
    }

}

項(xiàng)目中使用:Q模塊,和when來解決異步操作問題。它們是完整的Promise提議的實(shí)現(xiàn)。

var fs = require("fs");
var Q = require("Q");

var readFile = function ( file , encoding ) {
    
    var deferred = Q.defer();
    fs.readFile(file, encoding, deferred.makeNodeResolver());
    
    return deferred.promise; 
    
}

readFile("foo.txt", "utf-8").then(function ( data ) {
    
    // success
    console.log( data );
    
}, function ( err ) {
    
    // error
    console.log("error:" , err);
    
});

Promise中多異步協(xié)議

Promise解決的是:單個(gè)異步操作中存在的問題.

所有操作成功,這個(gè)異步操作才成功,一旦其中一個(gè)異步操作失敗,整個(gè)一部操作就失敗。

Promise的進(jìn)階知識(shí)

Promise缺點(diǎn):需要為不同的場(chǎng)景封裝不同的API,沒有直接的原生事件那么靈活。

Promise最主要的是處理隊(duì)列操作。
支持序列執(zhí)行的Promise

promise()
    .then(obj.api1)
    .then(obj.api2)
    .then(obj.api3)
    .then(function ( valu4 ) {
             // Do something with value4
    }, function ( error ) {

    })
    .done();

Promise支持鏈?zhǔn)綀?zhí)行步驟:
1:將所有的回調(diào)都存到隊(duì)列中
2:Promise完成時(shí),租個(gè)執(zhí)行回調(diào),一旦檢測(cè)返回了新的Promise對(duì)象,停止執(zhí)行,然后將當(dāng)前Deferred對(duì)象的promise引用改變?yōu)樾碌腜romise對(duì)象,并將隊(duì)列中的余下的回調(diào)轉(zhuǎn)交給它。

流程控制庫

尾觸發(fā)與Next

尾觸發(fā):手動(dòng)調(diào)用才能持續(xù)執(zhí)行后續(xù)調(diào)用,關(guān)鍵字next
應(yīng)用:Connect中間件中.Connect中間件傳遞請(qǐng)求對(duì)象,響應(yīng)對(duì)象和尾觸發(fā)函數(shù),通過隊(duì)列形成一個(gè)處理流。

// Connect的核心實(shí)現(xiàn)
function createServer () {
    
    function app ( req, res ) {
        app.handle(req,res);
    }
    
    utils.merge(app, proto);
    utils.merge(app, EventEmitter.prototype);
    
    app.route = "/";
    app.stack = []; // 核心代碼
    // stack屬性時(shí)服務(wù)器內(nèi)部維護(hù)的中間件隊(duì)列
    
    for ( var i=0; i

中間件這種尾觸發(fā)模式并不是要求每個(gè)中間方法都是異步,但如果每步驟都采用異步來完成,實(shí)際上是串行化的處理。
流式處理將一些串行的邏輯扁平化。

串行化:將對(duì)象存儲(chǔ)到介質(zhì)(如文件,內(nèi)存緩沖區(qū)等)中或是以二進(jìn)制方式通過網(wǎng)絡(luò)傳輸。然后需要使用的時(shí)候通過:反串行化從這些連續(xù)的字節(jié)數(shù)據(jù)重新構(gòu)建一個(gè)與原始對(duì)象狀態(tài)相同的對(duì)象。
作用:有時(shí)候,需要將對(duì)象的狀態(tài)保存下來,在需要時(shí)再會(huì)將對(duì)象恢復(fù)。
對(duì)象通過寫出描述自己的狀態(tài)數(shù)值來記錄自己,這個(gè)過程叫對(duì)象串行化。

async

流程控制模塊async

○異步的串行執(zhí)行

series(); 實(shí)現(xiàn)一組任務(wù)的串行執(zhí)行

var fs = require("fs");
var async = require("async");

// 異步串行執(zhí)行
async.series([function (cb) {
    
    fs.readFile("foo.txt", "utf-8", cb);
    
},function ( cb ) {
    
    fs.readFile("foo2.txt", "utf-8", cb);
    
}],function ( err, resluts ) {
    
    console.log( resluts );
    
});

// series(); 方法中傳入的函數(shù)cb();并非有使用者指定。此處的回調(diào)函數(shù)有async通過高階函數(shù)的方式注入。每個(gè)cb();執(zhí)行時(shí)會(huì)將結(jié)果保存起來,然后執(zhí)行下一個(gè)調(diào)用,知道結(jié)束所有調(diào)用。最終的回調(diào)函數(shù)執(zhí)行時(shí),隊(duì)列里的異步調(diào)用保存的結(jié)果以數(shù)組的方式傳入。
// 異常處理規(guī)則:一旦出現(xiàn)異常,就結(jié)束所有調(diào)用,并將異常傳遞給最終回調(diào)函數(shù)的第一個(gè)參數(shù)。

○異步的并行執(zhí)行
并行作用:提升性能

parallel(); 以并行執(zhí)行一些異步操作。

var fs = require("fs");
var async = require("async");

// 異步的并行執(zhí)行
async.parallel([function ( cb ) {
    
    fs.readFile("foo.txt", "utf-8", cb);
    
},function ( cb ) {
    
    fs.readFile("foo2.txt", "utf-8", cb);
    
}],function ( err, reslut ) {
    
    console.log( reslut );
    
});
// 通過注入的回調(diào)函數(shù)。
// parallel(); 對(duì)于異常的判斷依然是一旦某個(gè)異步調(diào)用產(chǎn)生了異常,就會(huì)將異常作為第一個(gè)參數(shù)傳遞給最終的回調(diào)函數(shù)。
    

○異步調(diào)用的依賴處理
series(); 適合無以來的異步串行。
缺點(diǎn):當(dāng)前一個(gè)的結(jié)果是后一個(gè)調(diào)用的輸入時(shí)。
使用:waterfall();

var fs = require("fs");
var async = require("async");

async.waterfall([function ( cb ) {
    
    fs.readFile("foo.txt", "utf-8", function ( err, content ) {
        
        cb(err, content);
        
    });
    
},function ( arg1, cb ) {
    
    fs.readFile("foo2.txt", "utf-8", function ( err, content ) {
        
        cb(err, content + arg1);
        
    });
    
}],function ( err, resluts ) {
    
    console.log( resluts );
    
});

○自動(dòng)依賴處理
auto(); 根據(jù)依賴自動(dòng)分析,以最佳的順序執(zhí)行業(yè)務(wù)。
實(shí)現(xiàn)復(fù)雜的依賴關(guān)系,業(yè)務(wù)中或是異步,或是同步。

var deps = {
    readConfig: function ( cb ) {
        cb();
    },
    connectMongoDB: ["readConfig", function ( cb ) {
        cb();
    }],
    connectRedis: ["readConfig", function ( cb ) {
        cb();
    }],
    complieAsserts: function ( cb ) {
        cb();    
    },
    uploadAsserts: ["complieAsserts", function (cb ) {
        cb();
    }],
    startup: ["connectMongoDB", "connectRedis", "uploadAsserts", function ( cb ) {
        // startup
    }]
}

async.auto(deps);

Step

Step只有一個(gè)接口Step.
Step(task1, task2, task3);
Step接受任意數(shù)量的任務(wù),所有任務(wù)都將會(huì)串行依次執(zhí)行。

var Step = require("step");
var fs = require("fs");

Step(function readFile1() {
    
    fs.readFile("foo.txt", "utf-8", this);
    
}, function readFile2( err, content ) {
    
    fs.readFile("foo2.txt", "utf-8", this);
    
}, function done( err, content ) {
    
    console.log( content );
    
});

// Step使用到了this關(guān)鍵字,它是Step內(nèi)部的一個(gè)next()方法,將異步調(diào)用的結(jié)果傳遞給下一個(gè)任務(wù)作為參數(shù),并調(diào)用執(zhí)行

○并行任務(wù)執(zhí)行
parael();方法,告知Step需要等到所有任務(wù)完時(shí)才進(jìn)行下一個(gè)任務(wù)。

var Step = require("step");
var fs = require("fs");

Step(function readFile1() {
    
    fs.readFile("foo.txt", "utf-8", this.parallel());
    fs.readFile("foo2.txt", "utf-8", this.parallel());
    
},function done( err, content1, content2 ) {
    
    console.log( arguments );
    
});
// 異常處理:一旦有一個(gè)異常產(chǎn)生,這個(gè)異常會(huì)作為下一個(gè)方法的第一個(gè)參數(shù)傳入。

○結(jié)果分組
this.group();
將返回的數(shù)據(jù)保存在數(shù)組中

異步并發(fā)控制

原因:并發(fā)量過大,下層服務(wù)器將會(huì)吃不消。如果對(duì)文件系統(tǒng)進(jìn)行大量并發(fā)調(diào)用,操作系統(tǒng)的文件描述符數(shù)量將會(huì)被瞬間用光。

bagpipe的解決方案

通過一個(gè)隊(duì)列來控制并發(fā)量

如果當(dāng)前活躍(指調(diào)用發(fā)起但為執(zhí)行回調(diào))的異步調(diào)用量小于限定值,從隊(duì)列中取出執(zhí)行。

如果活躍調(diào)用達(dá)到限定值,調(diào)用暫時(shí)存放在隊(duì)列中。

每個(gè)異步調(diào)用結(jié)束時(shí),從隊(duì)列中取出心兒異步調(diào)用執(zhí)行。

bagpipe中的push();和full事件
var Bagpipe = require("bagpipe");

// 設(shè)定最大并發(fā)數(shù)為10
var bagpipe = new Bagpipe(10);

for ( var i=0; i<100; i++ ) {
    
    bagpipe.push(async, function () {
        // 異步回調(diào)執(zhí)行
    });
    
}

bagpipe.on("full", function ( length ) {
    
    console.warn("底層系統(tǒng)處理不能及時(shí)完成,隊(duì)列擁堵,目前隊(duì)列長度為:"+ length);
    
});

拒絕模式
作用:大量異步調(diào)用需要分場(chǎng)景,需要實(shí)時(shí)方面就快速返回。
在設(shè)定并發(fā)數(shù)時(shí),參數(shù)設(shè)置為true

// 設(shè)定最大并發(fā)數(shù)為10
var bagpipe = new Bagpipe(10, {
    refuse: true
});

超時(shí)控制
原因:異步調(diào)用耗時(shí)太久,調(diào)用產(chǎn)生的速度遠(yuǎn)遠(yuǎn)高于執(zhí)行的速度。防止某些異步調(diào)用使用太多的時(shí)間。
實(shí)現(xiàn):將執(zhí)行時(shí)間太久的異步調(diào)用清理出活躍隊(duì)列。通過,設(shè)定時(shí)間閥值。
效果:排隊(duì)中的異步調(diào)用越快執(zhí)行。

var bagpipe = new Bagpipe(10, {
    timeout: 3000
});

async的解決方案
parallelLimit(); 處理異步調(diào)用的限制
缺點(diǎn):無法動(dòng)態(tài)增加并行任務(wù)。

var fs = require("fs");
var async = require("async");

async.parallelLimit([function ( cb ) {
    
    fs.readFile("foo.txt", "utf-8", cb);
    
},function ( cb ) {
    
    fs.readFile("foo2.txt", "utf-8", cb);
    
}], 1, function ( err, resluts ) { // 用于限制并發(fā)數(shù)量的參數(shù),任務(wù)只能同時(shí)并發(fā)一定數(shù)量,而不是無限制并發(fā)
    
    console.log( resluts );
    
});

queue(); 處理異步調(diào)用限制,能夠的動(dòng)態(tài)增加任務(wù)。
一般用于:遍歷文件目錄等操作

var q = async.queue(function ( file, cb ) {
    fs.readFile("foo.txt", "utf-8", cb);
}, 2);

q.drain = function () {
    
    // 完成對(duì)了中的所有任務(wù)
    
}

fs.readdirSync(".").forEach(function ( file ) {
    
    q.push(file, function ( err, data ) {
        
        // TODO
        
    });
    
});
內(nèi)存控制 V8的垃圾回收機(jī)制與內(nèi)存限制

JavaScript的垃圾回收機(jī)制是自動(dòng)進(jìn)行內(nèi)存管理。

V8的內(nèi)存限制

Node中通過JavaScript使用內(nèi)存時(shí)只能使用部分內(nèi)存(64位系統(tǒng)下約1.4GB,32位系統(tǒng)下約為0.7GB)
限制了無法操作大內(nèi)存對(duì)象。

Node中使用的JavaScript對(duì)象基本上都是通過V8自身的方式來進(jìn)行分配和管理。

V8為何限制了內(nèi)存使用量,需要回歸到V8在內(nèi)存使用上的策略。

V8的對(duì)象分配

在V8中,所有的JavaScript對(duì)象都是通過堆來進(jìn)行分配的。

查看內(nèi)存信息:

var usage = process.memoryUsage();

console.log( usage );
// { rss: 17170432, heapTotal: 8384512, heapUsed: 3787248 }
// rss  headTotal 已經(jīng)申請(qǐng)到堆內(nèi)存,   heapUsed 當(dāng)前使用量 // 單位:bytes

當(dāng)代碼中聲明變量并賦值時(shí),所使用對(duì)象的內(nèi)存就分配在堆中。如果已經(jīng)申請(qǐng)的堆空想內(nèi)存不夠分配新的對(duì)象,將繼續(xù)申請(qǐng)堆內(nèi)存,直到堆的代銷超過V8的限制為止。

V8限制堆大小原因:

V8最初為瀏覽器而設(shè)計(jì),不太可能使用到大量內(nèi)存的場(chǎng)景。

V8垃圾回收機(jī)制的限制。

1.5GB的垃圾回收堆內(nèi)存為例,V8做一次小的垃圾回收需要50毫秒以上,做一次非增量式的垃圾回收甚至要1秒以上。這是垃圾回收中引起JavaScript線程暫停執(zhí)行的時(shí)間,在這樣的時(shí)間花銷下,應(yīng)用的性能和響應(yīng)能力都會(huì)直線下降。考慮直接限制堆內(nèi)存。

Node可以在啟動(dòng)調(diào)節(jié)內(nèi)存限制大小

node --max-old-spce-size=1700 test.js // 單位MB
node --max-new-spce-size=1024 test.js // 單位 KB
//  一旦生效就不能再動(dòng)態(tài)改變。

V8的垃圾回收機(jī)制

1:V8主要的垃圾回收算法
V8垃圾回收策略主要基于分代式垃圾回收機(jī)制。
實(shí)際應(yīng)用中,對(duì)象的生存周期長短不一,不同的算法只能針對(duì)特定情況具有最好的效果。

V8的內(nèi)存分代

在V8中,主要講內(nèi)存分為新生代和老生代。
新生代:對(duì)象為存活時(shí)間較短對(duì)象。 --max-new-space-size命令行參數(shù)設(shè)置 新生代內(nèi)存空間大?。ㄗ畲笾担?4位操作系統(tǒng)32MB,32位操作系統(tǒng)16MB)
老生代:對(duì)象為存活較長或常駐內(nèi)存的對(duì)象。--max-old-spce-size命令行參數(shù)設(shè)置 老生代內(nèi)存空間大小。(64位操作系統(tǒng)下:1400MB,32位系統(tǒng)700MB)

使用缺陷:在啟動(dòng)Node時(shí)就指定,無法根據(jù)V8內(nèi)存使用情況自動(dòng)擴(kuò)充。

默認(rèn)情況下,V8堆內(nèi)存最大值:64位操作系統(tǒng):1464MB,32位操作系統(tǒng)732MB。(4 * reserved_semispce_size + max_old_generation_size_)

○ Scavenge算法
新生代中的對(duì)象主要通過Scavenge算法進(jìn)行垃圾回收。
Scavenge采用了:Cheney算法。

Cheney算法:復(fù)制的方式實(shí)現(xiàn)的垃圾回收算法。
將堆內(nèi)存一分為二,每一部分空間成為semispce。這兩個(gè)semispce空間中,只有一個(gè)處于試用,另一個(gè)處于閑置狀態(tài)。處于使用狀態(tài)的semispce空間稱成為From空間。處于限制狀態(tài)的空間成為To空間。

分配原則:分配對(duì)象時(shí),顯示在Form空間中進(jìn)行分配,當(dāng)開始進(jìn)行垃圾回收時(shí),會(huì)先檢查From空間中的存活對(duì)象,這些存活對(duì)象將被復(fù)制到To空間中,而非存活對(duì)象占用的空間將被釋放。完成賦值后,F(xiàn)orm空間和To空間的角色發(fā)生對(duì)換。

垃圾回收的過程中,就是通過將存活對(duì)象在兩個(gè)semispce空間之間進(jìn)行復(fù)制。

Scavenge缺點(diǎn):只能使用堆內(nèi)存中的一半內(nèi)存。
優(yōu)點(diǎn):時(shí)間效率高 (犧牲空間換取時(shí)間)
合適于新生代對(duì)象中,因?yàn)椋盒律械膶?duì)象的生命周期比較短。

當(dāng)一個(gè)對(duì)象進(jìn)過多次賦值依然存活時(shí),它將會(huì)被認(rèn)為是生命周期較長的對(duì)象。會(huì)被轉(zhuǎn)移到老生代中。采用新的算法進(jìn)行管理。

From空間中的存活對(duì)象在復(fù)制到TO空間之前需要進(jìn)行檢查。在一定條件下,需要將存活周期長的對(duì)象移動(dòng)到老生代中,也就是完成對(duì)象晉升。

晉升條件:

經(jīng)歷一次Scavenge回收

在默認(rèn)情況下,V8的對(duì)象分配主要集中在From空間中,對(duì)象從From空間中復(fù)制到To空間時(shí),會(huì)檢查它的內(nèi)存地址來判斷這個(gè)對(duì)象是否已經(jīng)經(jīng)歷過一次Scavenge回收。如果經(jīng)歷過了,就將該對(duì)象從From空間復(fù)制到老生代空間中,如果沒有,則復(fù)制到TO空間中。

To空間的內(nèi)存占用比。

當(dāng)要從From空間復(fù)制一個(gè)對(duì)象到To空間時(shí),如果To空間已經(jīng)使用超過25%,則這個(gè)對(duì)象直接晉升到老生代空間中。(25%限制值的原因,當(dāng)這個(gè)次Scavenge回收完成后,To空間將會(huì)變成From空間,接下來的內(nèi)存分配將在這個(gè)空間中進(jìn)行。)

○ Mark-Sweep & Mark-Compact

存活對(duì)象占用較大比重,采用Scavenge方式產(chǎn)生問題:
1:存活對(duì)象較多,復(fù)制存活對(duì)象的效率將會(huì)很低
2: 浪費(fèi)一般空間的問題。

Mark-Sweep標(biāo)記清除:標(biāo)記和清除,兩個(gè)階段。
Mark-Sweep在標(biāo)記階段遍歷堆中所有對(duì)象,并標(biāo)記著活著的對(duì)象。在隨后的清除階段只清除沒有被標(biāo)記的對(duì)象。
產(chǎn)生問題:進(jìn)行一次標(biāo)記清除回收后,內(nèi)存會(huì)出現(xiàn)不連續(xù)的狀態(tài)。
原因:內(nèi)存碎片會(huì)對(duì)后續(xù)的內(nèi)存分配造成問題,很可能出現(xiàn)需要分配一個(gè)大對(duì)象的情況,所有的碎片空間無法完成此次分配。就會(huì)提前出發(fā)垃圾回收。

Mark-Compact 標(biāo)記整理
解決:Mark-Sweep內(nèi)存碎片問題。
對(duì)象標(biāo)記死亡后,在整理的過程中,將或者對(duì)象往一端移動(dòng),移動(dòng)完成后,直接清理邊界的內(nèi)存。
缺點(diǎn):速度慢。

V8主要使用Mark-Sweep,在空間不足以對(duì)從新生代中晉升過來的對(duì)象進(jìn)行分配時(shí)才使用Mark-Compact

○ Incremental Marking (增量標(biāo)記)
全停頓:將應(yīng)用邏輯展亭下來,待執(zhí)行完來回收后再回復(fù)執(zhí)行應(yīng)用邏輯。

為了避免出現(xiàn)JavaScript應(yīng)用邏輯與垃圾回收器看到的不一致的情況。三種垃圾回收機(jī)制都采用全停頓。

解決辦法:增量標(biāo)記
垃圾回收與應(yīng)用邏輯交替執(zhí)行直到標(biāo)記階段完成。
除了增量標(biāo)記,V8還引入延遲清理,增量式清理。
讓清理與整理動(dòng)過也變成增量式。

V8對(duì)內(nèi)存限制的設(shè)置對(duì)于Chrome瀏覽器這種每個(gè)選項(xiàng)卡頁面使用一個(gè)V8實(shí)例而言,內(nèi)存的使用時(shí)綽綽有余。
Node編寫服務(wù)端來說,V8垃圾回收特點(diǎn)和JavaScript在單線程上的執(zhí)行情況,垃圾回收時(shí)影響性能的因素之一。
高新能的執(zhí)行效率,需要主要讓垃圾回收盡量少的進(jìn)行,尤其是全堆垃圾回收。

○ 查看垃圾回收日志

node --tarce_gc -e "var a = []; for (var i=0; i<1000000; i++) a.psuh(new Array(100))" > gc.log
// 垃圾回收日志信息

node --prof test.js
// 垃圾回收時(shí)所占用的時(shí)間
高效使用內(nèi)存

作用域

在JavaScript中能形成作用域的有函數(shù),width以及全局作用域。

var foo = function () {
    var local = {};
}

內(nèi)存回收過程
foo(); 函數(shù)在每次被調(diào)用時(shí)會(huì)創(chuàng)建對(duì)應(yīng)的作用域,函數(shù)執(zhí)行結(jié)束后,該作用域?qū)?huì)銷毀。
同時(shí)作用域中聲明的局部變量分配在該作用域上,隨著作用域的銷毀而銷毀。只被局部變量引用的對(duì)象存活周期較短。
由于對(duì)象非常小,將會(huì)分配在新生代的From空間中,在作用域釋放后,局部變量的elocal失效,其引用的對(duì)象將會(huì)在下次垃圾回收時(shí)被釋放。

標(biāo)識(shí)符查找

與作用域相關(guān)的即是標(biāo)識(shí)符查找。

作用域鏈

變量的主動(dòng)釋放。

如果變量是全局變量,(不通過var聲明或定義在global變量上),由于全局作用域需要直到進(jìn)程退出才能釋放,導(dǎo)致引用的對(duì)象常駐內(nèi)存(常駐在老生代中)。如果需要釋放常駐內(nèi)存的對(duì)象,可以通過delete操作符刪除引用關(guān)系?;蛘邔⒆兞恐匦沦x值,讓舊的對(duì)象脫離引用關(guān)系。
在非全局作用域中,想主動(dòng)釋放變量引用的對(duì)象,也可以通過delete和重新賦值。但是V8中通過delete刪除對(duì)象的屬性有可能干擾V8的優(yōu)化,通過賦值方式解除引用更好。

閉包

作用域鏈上的對(duì)象訪問只能向上,外部無法向內(nèi)部訪問。
閉包:實(shí)現(xiàn)外部作用域訪問內(nèi)部作用域的中的變量的方法。
高階函數(shù)特性:函數(shù)可以作為參數(shù)或者返回值。

閉包問題:
一旦有變量應(yīng)用這個(gè)中間函數(shù),這個(gè)中間函數(shù)將不會(huì)釋放,同時(shí)也會(huì)使原始的作用域不會(huì)得到釋放,作用域中產(chǎn)生的內(nèi)存占用也不會(huì)得到釋放。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/80025.html

相關(guān)文章

  • 深入淺出node.js總結(jié)-模塊機(jī)制(1)

    摘要:先天就缺乏一項(xiàng)功能模塊通過標(biāo)簽引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力。與文件模塊區(qū)別地方在于它從內(nèi)存中加載緩存執(zhí)行結(jié)果的位置核心模塊在對(duì)象上,文件模塊在對(duì)象上未完待續(xù) javascript先天就缺乏一項(xiàng)功能:模塊 javasciprt 通過script標(biāo)簽引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力。人們不得不用命名空間等方式人為地約束代碼,以求達(dá)到安全和易用的...

    jifei 評(píng)論0 收藏0
  • node模塊加載層級(jí)優(yōu)化

    摘要:環(huán)境變量法通過上一節(jié)的源碼分析,我們知道了的作用,那么如何使用或者優(yōu)雅的使用來解決依賴加載問題呢嘗試一最為直接的是,修改系統(tǒng)的環(huán)境變量。 模塊加載痛點(diǎn) 大家也或多或少的了解node模塊的加載機(jī)制,最為粗淺的表述就是依次從當(dāng)前目錄向上級(jí)查詢node_modules目錄,若發(fā)現(xiàn)依賴則加載。但是隨著應(yīng)用規(guī)模的加大,目錄層級(jí)越來越深,若是在某個(gè)模塊中想要通過require方式以依賴名稱或相對(duì)路...

    eccozhou 評(píng)論0 收藏0
  • 深入node之Transform

    摘要:內(nèi)部架構(gòu)上圖表示一個(gè)實(shí)例的組成部分部分緩沖數(shù)組內(nèi)部函數(shù)部分緩沖鏈表內(nèi)部函數(shù)實(shí)例必須實(shí)現(xiàn)的內(nèi)部函數(shù)以及系統(tǒng)提供的回調(diào)函數(shù)。有三個(gè)參數(shù),第一個(gè)為待處理的數(shù)據(jù),第二個(gè)為編碼,第三個(gè)為回調(diào)函數(shù)。 Transform流特性 在開發(fā)中直接接觸Transform流的情況不是很多,往往是使用相對(duì)成熟的模塊或者封裝的API來完成流的處理,最為特殊的莫過于through2模塊和gulp流操作。那么,Tra...

    williamwen1986 評(píng)論0 收藏0
  • 深入淺出nodeJS - 4 - (玩轉(zhuǎn)進(jìn)程、測(cè)試、產(chǎn)品化)

    摘要:進(jìn)程間通信的目的是為了讓不同的進(jìn)程能夠互相訪問資源,并進(jìn)程協(xié)調(diào)工作。這個(gè)過程的示意圖如下端口共同監(jiān)聽集群穩(wěn)定之路進(jìn)程事件自動(dòng)重啟負(fù)載均衡狀態(tài)共享模塊工作原理事件二測(cè)試單元測(cè)試性能測(cè)試三產(chǎn)品化項(xiàng)目工程化部署流程性能日志監(jiān)控報(bào)警穩(wěn)定性異構(gòu)共存 內(nèi)容 9.玩轉(zhuǎn)進(jìn)程10.測(cè)試11.產(chǎn)品化 一、玩轉(zhuǎn)進(jìn)程 node的單線程只不過是js層面的單線程,是基于V8引擎的單線程,因?yàn)?,V8的緣故,前后...

    henry14 評(píng)論0 收藏0
  • 深入Vue.js從源碼開始(三)

    摘要:數(shù)據(jù)驅(qū)動(dòng)一個(gè)核心思想是數(shù)據(jù)驅(qū)動(dòng)。發(fā)生了什么從入口代碼開始分析,我們先來分析背后發(fā)生了哪些事情。函數(shù)最后判斷為根節(jié)點(diǎn)的時(shí)候設(shè)置為,表示這個(gè)實(shí)例已經(jīng)掛載了,同時(shí)執(zhí)行鉤子函數(shù)。這里注意表示實(shí)例的父虛擬,所以它為則表示當(dāng)前是根的實(shí)例。 數(shù)據(jù)驅(qū)動(dòng) Vue.js 一個(gè)核心思想是數(shù)據(jù)驅(qū)動(dòng)。所謂數(shù)據(jù)驅(qū)動(dòng),是指視圖是由數(shù)據(jù)驅(qū)動(dòng)生成的,我們對(duì)視圖的修改,不會(huì)直接操作 DOM,而是通過修改數(shù)據(jù)。它相比我們傳...

    lentrue 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<