我讀過這fs.readFile是一個異步操作,并且讀取發生在主執行緒以外的單獨執行緒中,因此主執行緒執行不會被阻塞。所以我想試試下面的東西
// reads take almost 12-15ms
fs.readFile('./file.txt', (err, data) => {
console.log('FIRST READ', Date.now() - start)
const now = Date.now()
fs.readFile('./file.txt', (err, data) => {
// logs how much time it took from the beginning
console.log('NESTED READ CALLBACK', Date.now() - start)
})
// blocks untill 20ms more, untill the above read operation is done
// so that the above read operation is done and another callback is queued in the poll phase
while (Date.now() - now < 20) {}
console.log('AFTER BLOCKING', Date.now() - start)
})
我正在fs.readFile父呼叫的回呼中進行另一個fs.readFile呼叫。我期望的是日志NESTED READ CALLBACK必須立即到達,AFTER BLOCKING因為讀取必須在單獨的執行緒中異步完成
結果日志NESTED READ CALLBACK在AFTER BLOCKING呼叫后 15 毫秒出現,表明當我在 while 回圈中阻塞時,異步讀取操作不知何故從未發生。順便說一下,while 回圈是用來模擬一些需要 20 毫秒才能完成的任務
這里到底發生了什么?還是我在這里遺漏了一些資訊?順便說一句,我正在使用 Windows
uj5u.com熱心網友回復:
在您的while()回圈期間,不會處理 Javascript 中的任何事件,并且您的任何 Javascript 代碼都不會運行(因為您只是通過回圈阻止了事件回圈的處理)。
while磁盤操作可以取得一些進展(因為它們在系統執行緒中做一些作業),但在回圈完成之前不會處理它們的結果。但是,因為fs.readFile()實際上由三個或更多操作、fs.open()和fs.read()組成fs.close(),所以當事件回圈被阻塞時,它可能不會走得太遠,因為它需要處理事件以推進其作業的不同階段。
結果發現日志 NESTED READ CALLBACK 在 AFTER BLOCKING 呼叫后 15 毫秒出現,這表明當我在 while 回圈中阻塞時,異步讀取操作從未發生過。順便說一下,while 回圈是用來模擬一些需要 20 毫秒才能完成的任務
這里到底發生了什么?
fs.readFile()不是單一的單體操作。相反,它由和 組成fs.open(),fs.read()并且fs.close()它們的排序在主執行緒中的用戶級 Javascript 中運行。因此,當您使用while()回圈阻塞主執行緒時,fs.readFile()無法取得很大進展。可能會發生什么是您啟動第二個fs.readFile()操作并啟動該fs.open()操作。它被發送到 libuv 執行緒池中的作業系統執行緒。然后,你用你的回圈阻塞事件while()回圈。當該回圈阻塞事件回圈時,fs.open()完成和(在 libuv 事件回圈內部)一個事件被放置到事件佇列中以呼叫完成回呼fs.open()稱呼。但是,事件回圈被您的回圈阻塞,因此無法立即呼叫回呼。因此,完成操作的任何進一步進展fs.readFile()都會被阻止,直到事件回圈釋放并可以再次處理等待事件。
當您的while()回圈完成時,控制權將回傳到事件回圈,呼叫的完成回呼將fs.open()被呼叫,而隨后將啟動從檔案中讀取實際資料。
僅供參考,您實際上可以在Github 存盤庫中fs.readFile()自己檢查代碼。如果您遵循它的流程,您會看到,從它自己的 Javascript 中,它首先呼叫本機代碼操作,然后,當它完成并且 Javascript 能夠通過事件佇列處理完成事件時,它將運行函式將呼叫(另一個本機代碼操作),然后,當它完成并且 Javascript 能夠通過事件佇列處理完成事件時,它將呼叫 `readFileAfterStat(...) 這將分配一個緩沖區并啟動讀取手術。binding.open()readFileAfterOpen(...)bind.fstat()
在這里,隨著流程跳到最終它呼叫的read_file_context物件,代碼變得更難理解將檔案中的所有位元組放入緩沖區,關閉檔案,然后呼叫最終回呼。read()
所有這些細節的重點是說明如何fs.readFile()用 Javascript 撰寫自身并由多個步驟組成(其中一些呼叫代碼,將在不同的執行緒中使用一些本機代碼),但只能從一個步驟前進到下一步,當事件回圈能夠處理新事件。因此,如果您使用回圈阻塞事件回圈while,那么fs.readFile()將卡在步驟之間并且無法前進。只有當事件回圈能夠處理事件時,它才能前進并最終完成。
類比
這是一個簡單的類比。你請你哥哥幫你一個忙,去三家店給你取東西吃晚飯。你給他第一家商店的清單和目的地,然后讓他在第一家商店完成后用手機給你打電話,你會給他第二個目的地和那家商店的清單。他前往第一家商店。
與此同時,你用手機給你的女朋友打電話,開始和她進行長時間的交談。你的兄弟在第一家商店結束并給你打電話,但你忽略了他的電話,因為你還在和你的女朋友說話。你的兄弟被困在跑腿的使命上,因為他需要和你談談以了解下一步是什么。
在這個類比中,手機有點像事件回圈處理下一個事件的能力。如果你阻止他打電話給你,那么他就無法進行下一步(事件回圈被阻止)。他對這三家店的走訪,就像是執行fs.readfile()操作所涉及的各個步驟。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/428880.html
