15

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

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

4

1 回答 1

21
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,node_#####.cc 土地。binding('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));    \
  }

请注意,SYNC_CALL 使用不同的 req-wrap。这是 ASYNC 方法的相关 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 解释环境来单独处理这个特定的调用。简而言之,如果不构建/修改您自己的 node 版本,您就无法实现自己的异步/同步调用版本,就像 node 一样。话虽如此,异步实际上只适用于 I/O 操作。也许你认为你需要更同步的事情的描述将是有序的。一般来说,如果你认为 node 不支持你想做的事情,你只是没有接受回调机制来充分发挥它的潜力。

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

强迫一种语言以它不想处理的方式处理事情是编写糟糕代码的好方法。

于 2013-06-27T14:13:38.460 回答