如果nodejs使用非阻塞IO,fs.readFileSync是如何实现的?

guy*_*abi 13 nonblocking blocking node.js

我在文件系统库中看到了很多同步函数.如fs.readFileSync(filename, [options]).

如果节点具有异步/非阻塞IO并且没有睡眠方法,那么如何(以及为什么)实现这些功能 - 我可以使用相同的机制来实现其他同步功能吗?

Chr*_*sCM 17

fs.readFileSync()
Run Code Online (Sandbox Code Playgroud)

真的只是一个包装

fs.readSync() 
Run Code Online (Sandbox Code Playgroud)

功能.所以问题是fs.readSync()与fs.read()相比如何实现.如果你看一下这两个函数的实现,他们都会利用绑定模块.在这种情况下,初始化为

var binding = process.binding('fs').  
Run Code Online (Sandbox Code Playgroud)

和电话是

binding.read(fd, buffer, offset, length, position, wrapper);//async
var r = binding.read(fd, buffer, offset, length, position);//sync
Run Code Online (Sandbox Code Playgroud)

分别.一旦我们进入"绑定"模块,我们就进入v8,node _ #####.cc land.绑定('fs')的实现可以在node_file.cc中的节点存储库代码中找到.节点引擎为C++调用提供重载,一个进行回调,一个不进行回调.node_file.cc代码利用了req_wrap类.这是v8引擎的包装器.在node_file.cc中我们看到:

#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));    \
  }
Run Code Online (Sandbox Code Playgroud)

请注意,SYNC_CALL使用不同的req-wrap.以下是在req_wrap.h中找到的ASYNC方法的相关req_wrap构造函数的代码

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_);
  }
Run Code Online (Sandbox Code Playgroud)

请注意,此函数正在创建一个新的v8范围对象来处理此事件的运行.这是异步内容的异步部分发生的地方.v8引擎启动了一个新的javascript解释环境来单独处理这个特定的调用.简而言之,在不构建/修改自己的节点版本的情况下,您无法以与节点相同的方式实现自己的异步/同步调用版本.话虽这么说,异步实际上只适用于I/O操作.也许是为了说明为什么你认为你需要更加同步的东西.一般来说,如果您认为节点不支持您想要做的事情,那么您就不会采用回调机制来充分发挥它的潜力.

话虽这么说,如果您需要异步行为,可以考虑使用事件节点模块来实现自己的事件处理程序.如果你迫切需要同步做的事情,你可以考虑原生扩展,但是,我强烈建议不要这样做.考虑如何在异步事件循环中工作以获得您需要以这种方式完成的操作.拥抱这种思维方式,或切换到另一种语言.

强制语言以不想处理它的方式处理事物是编写错误代码的好方法.