2013-06-27 53 views
10

我在file system library中看到很多同步功能。如fs.readFileSync(filename, [options])如果nodejs使用非阻塞IO,fs.readFileSync如何實現?

如果節點具有異步/非阻塞IO並且沒有睡眠方法,那麼這些函數是如何實現的(以及爲什麼) - 我可以使用相同的機制來實現其他同步函數嗎?

+0

好問題!也許有人需要重新創建javascript的協同程序,並公開事件循環以允許在node.js中編寫同步非阻塞代碼。 – Alex

回答

15
fs.readFileSync() 

實際上只是爲

fs.readSync() 

功能的包裝。所以問題是fs.readSync()與fs.read()相比如何實現。如果你看看這兩個函數的實現,它們都利用綁定模塊。在這種情況下被初始化爲

var binding = process.binding('fs'). 

和電話是

binding.read(fd, buffer, offset, length, position, wrapper);//async 
var r = binding.read(fd, buffer, offset, length, position);//sync 

分別。一旦我們進入「綁定」模塊,我們就會在v8,節點_ #####。cc land。綁定('fs')的實現可以在node_file.cc的節點存儲庫代碼中找到。節點引擎爲C++調用提供重載,一個調用回調,一個不調用回調。 node_file.cc代碼利用了req_wrap類。這是v8引擎的包裝。在node_file.cc我們看到:該SYNC_CALL使用不同的REQ纏繞

#define ASYNC_CALL(func, callback, ...)       \ 
    FSReqWrap* req_wrap = new FSReqWrap(#func);      \ 
    int r = uv_fs_##func(uv_default_loop(), &req_wrap->req_,  \ 
     __VA_ARGS__, After);          \ 
    req_wrap->object_->Set(oncomplete_sym, callback);    \ 
    req_wrap->Dispatched();           \ 
    if (r < 0) {             \ 
    uv_fs_t* req = &req_wrap->req_;        \ 
    req->result = r;            \ 
    req->path = NULL;            \ 
    req->errorno = uv_last_error(uv_default_loop()).code;   \ 
    After(req);             \ 
    }                \ 
    return scope.Close(req_wrap->object_); 

#define SYNC_CALL(func, path, ...)        \ 
    fs_req_wrap req_wrap;           \ 
    int result = uv_fs_##func(uv_default_loop(), &req_wrap.req, __VA_ARGS__, NULL); \ 
    if (result < 0) {            \ 
    int code = uv_last_error(uv_default_loop()).code;    \ 
    return ThrowException(UVException(code, #func, "", path)); \ 
    } 

通知。下面是相關req_wrap構造函數異步方法的代碼,在req_wrap.h發現

ReqWrap() { 
    v8::HandleScope scope; 
    object_ = v8::Persistent<v8::Object>::New(v8::Object::New()); 

    v8::Local<v8::Value> domain = v8::Context::GetCurrent() 
            ->Global() 
            ->Get(process_symbol) 
            ->ToObject() 
            ->Get(domain_symbol); 

    if (!domain->IsUndefined()) { 
     // fprintf(stderr, "setting domain on ReqWrap\n"); 
     object_->Set(domain_symbol, domain); 
    } 

    ngx_queue_insert_tail(&req_wrap_queue, &req_wrap_queue_); 
    } 

注意,此功能是創建一個新的V8範圍對象來處理這個事件的運行。這是異步事件的異步部分發生的地方。 v8引擎啓動一個新的JavaScript解釋環境來分別處理這個特定的調用。簡而言之,在沒有構建/修改自己的節點版本的情況下,無法以與節點相同的方式實現自己的異步/同步版本的調用。這就是說,異步真的只適用於I/O操作。也許你爲什麼認爲你需要更加同步的東西的描述是正確的。一般來說,如果您認爲節點不支持您想要執行的操作,那麼您只是沒有采用回調機制來充分發揮其潛力。這就是說,你可以考慮使用事件節點模塊來實現你自己的事件處理程序,如果你需要異步行爲。你可以考慮本地擴展,如果有什麼事情你需要同步做,但是,我強烈建議不要這樣做。考慮如何在異步事件循環內工作以獲得您需要以這種方式完成的工作。擁抱這種思維方式,或切換到另一種語言。

強制語言處理事物的方式不想處理它們是編寫錯誤代碼的絕佳方法。

+1

非常全面的答案。 「強迫一種語言來處理不想處理的東西」 - 這是一個有趣的措辭。這不正是nodeJS人正在用'fs.readFileSync'做什麼?它爲什麼存在?畢竟,在節點中同步調用是否還有空間? –

+0

Stackoverflow ...永遠不知道你何時向他人遞交他們不準備使用的工具。最後一句話(爲什麼要在事實庫中選擇一個有意見的句子???)是一個免責聲明......嘿,我不支持嘗試修改Node的想法!但是,如果你必須這樣做,你就是這麼做的。你可能是對的,可能還有更多同步調用的空間,並讓開發人員決定使用哪一個,但是現在,Node的開發人員已經認定情況並非如此,這正是我的觀點。 – ChrisCM

+0

根據您的問題,同步FS方法存在有兩個原因。答:作爲初始化步驟的一部分收集配置信息是一個相對普遍的需求,並且同步更簡單。 B:在某些情況下,系列同步磁盤請求實際上比使用一堆不準備處理的異步請求溢出磁盤更高效。服務器IO(DDOS攻擊)也是如此,它只是無限(如10000x)難以創建的情況。但是,單個進程很容易使磁盤驅動器過載並使其速度呈指數級下降。 – ChrisCM