疑問
最近在知乎上面看到一條有趣的關(guān)于node.js面試問題
Node 里有readFile和對(duì)應(yīng)的同步方法readFileSync术唬,但http.get() 卻沒有 http.getSync(),如果要實(shí)現(xiàn)一個(gè)http.getSync()溉浙,怎么做奈惑?
http://www.zhihu.com/question/24648388
圍繞這個(gè)問題來展開我們的思考矛缨,文件讀取是node.js最重要的io模塊拐叉,也是最特別的模塊毫深,他的特別在于它是唯一一個(gè)擁有異步和同步,readFile
異步驾诈,readFileSync
同步缠诅,兩種api的模塊,相信每一個(gè)nodejser 都會(huì)覺得是不是意味著乍迄,我們能在node.js里面寫出同步的處理函數(shù)管引?本文將會(huì)圍繞這個(gè)核心問題來展開分析。
API
fs.readFile 異步讀取
js基本流程
通過fs.stats 讀取文件大小闯两,當(dāng)文件大小等于0的時(shí)候褥伴,可能是內(nèi)核函數(shù)無法正確讀取文件大小,對(duì)于這種情況漾狼,node.js 會(huì)進(jìn)行分片讀取重慢,每次異步讀取8k內(nèi)容到buffer里,并將buffer的內(nèi)容存儲(chǔ)到數(shù)組逊躁,直到文件無法讀取為止似踱,同時(shí)會(huì)在無法讀取的時(shí)候,將剛才8k們的數(shù)組稽煤,合并為一個(gè)buffer返回結(jié)果核芽,對(duì)于能夠正確讀取文件大小的時(shí)候,則直接生成與文件大小相同酵熙,并將內(nèi)容讀取到buffer里面轧简,當(dāng)size大于4g的時(shí)候,直接報(bào)錯(cuò)返回匾二。
fs.readFileSync 同步讀取
主要流程與異步相同哮独,唯一不同的地方,是readFileSync 不帶回調(diào)參數(shù)察藐,具體結(jié)果是直接返回借嗽。
底層實(shí)現(xiàn)
由此可見,究竟執(zhí)行的方式是異步還是同步转培,問題的核心在與底層實(shí)現(xiàn)恶导,讓我們看看libuv是怎么說的。
截取libuv的原話浸须,是怎么說的
The libuv filesystem operations are different from socket operations. Socket operations use the non-blocking operations provided by the operating system. Filesystem operations use blocking functions internally, but invoke these functions in a thread pool and notify watchers registered with the event loop when application interaction is required.
libuv的文件操作方法跟socket的實(shí)現(xiàn)方式原理不一樣惨寿,對(duì)于socket的處理,libuv是調(diào)用系統(tǒng)本身的非阻塞特性删窒, 而fs模塊裂垦,則是調(diào)用通過線程池來模擬,監(jiān)聽線程池的任務(wù)肌索,當(dāng)任務(wù)完成后蕉拢,回調(diào)信息給調(diào)用者。
而最終libuv采用異步,還是同步的方式的途徑晕换,是根據(jù) 調(diào)用的參數(shù)決定的
UV_EXTERN int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, size_t length, int64_t offset, uv_fs_cb cb);
當(dāng)callback為空的時(shí)候午乓,函數(shù)同步執(zhí)行,當(dāng)過callback存在函數(shù)的時(shí)候闸准,異步執(zhí)行益愈。
總結(jié)
getSync 這種方法,在libuv框架下是無法實(shí)現(xiàn)的夷家,libuv從底層調(diào)用開始就是非阻塞的蒸其,是操作系統(tǒng)提供,所以你無論用什么黑魔法實(shí)際效果都是異步库快,但對(duì)于fs模塊來說摸袁,僅僅是利用線程池模擬出異步的效果,所以能义屏,非系統(tǒng)自帶的特性但惶,所以能寫出同步的api。