fs.readFileSync()
thực sự chỉ là một wrapper cho hàm
fs.readSync()
. Vì vậy, câu hỏi là fs.readSync() được thực hiện như thế nào so với fs.read(). Nếu bạn nhìn vào việc triển khai thực hiện hai hàm này, chúng đều tận dụng lợi thế của mô-đun kết buộc. Trong trường hợp này được intialized để
var binding = process.binding('fs').
và các cuộc gọi được
binding.read(fd, buffer, offset, length, position, wrapper);//async
var r = binding.read(fd, buffer, offset, length, position);//sync
tương ứng. Một khi chúng ta đang ở trong mô-đun "ràng buộc", chúng tôi đang ở trong v8, nút _ #####. Cc đất. Việc thực hiện ràng buộc ('fs') có thể được tìm thấy trong mã kho lưu trữ nút, trong node_file.cc. Công cụ nút cung cấp quá tải cho các cuộc gọi C++, một cuộc gọi lại, một cuộc gọi không thực hiện. Mã node_file.cc tận dụng lợi thế của lớp req_wrap. Đây là trình bao bọc cho động cơ v8. Trong node_file.cc, chúng ta thấy điều này:
#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)); \
}
Lưu ý rằng SYNC_CALL sử dụng một gói khác. Dưới đây là mã cho các nhà xây dựng req_wrap có liên quan đối với phương pháp ASYNC, được tìm thấy trong 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_);
}
Chú ý rằng chức năng này đang tạo ra một đối tượng phạm vi v8 mới để xử lý các hoạt động của sự kiện này. Đây là nơi phần không đồng bộ của công cụ không đồng bộ xảy ra. Công cụ v8 khởi chạy một môi trường giải thích javascript mới để xử lý cuộc gọi cụ thể này một cách riêng biệt. Tóm lại, nếu không xây dựng/sửa đổi phiên bản nút của riêng bạn, bạn không thể thực hiện các phiên bản cuộc gọi không đồng bộ/đồng bộ của riêng mình, giống như cách mà nút đó thực hiện. Điều đó đang được nói, không đồng bộ thực sự chỉ áp dụng cho các hoạt động I/O. Có lẽ một mô tả lý do tại sao bạn nghĩ rằng bạn cần những thứ để được đồng bộ hơn sẽ được theo thứ tự. Nói chung, nếu bạn tin rằng nút không hỗ trợ điều gì đó bạn muốn làm, bạn chỉ cần không nắm lấy cơ chế gọi lại để nó có tiềm năng đầy đủ.
Điều đó đang được nói, bạn có thể xem xét sử dụng mô-đun nút sự kiện để triển khai trình xử lý sự kiện của riêng mình nếu bạn cần hành vi không đồng bộ. Và bạn có thể xem xét các phần mở rộng tự nhiên nếu có những thứ bạn tuyệt vọng cần phải làm đồng bộ, tuy nhiên, tôi khuyên bạn nên chống lại điều này. Hãy xem xét cách bạn có thể làm việc trong vòng lặp sự kiện không đồng bộ để có được những gì bạn cần làm theo cách này. Chấp nhận phong cách suy nghĩ này, hoặc chuyển sang ngôn ngữ khác.
Bắt buộc ngôn ngữ xử lý mọi thứ theo cách mà nó không muốn xử lý chúng là cách tuyệt vời để viết mã xấu.
Câu hỏi hay! có lẽ một người sẽ cần phải tái tạo lại coroutines cho javascript và phơi bày vòng lặp sự kiện để cho phép viết mã không chặn đồng bộ trong node.js. – Alex