ファイル システム ライブラリには多くの同期関数があります。などfs.readFileSync(filename, [options])
。
ノードに非同期/非ブロッキング IO があり、スリープ メソッドがない場合、これらの関数はどのように (そしてなぜ) 実装されますか? 同じメカニズムを使用して他の同期関数を実装できますか?
ファイル システム ライブラリには多くの同期関数があります。などfs.readFileSync(filename, [options])
。
ノードに非同期/非ブロッキング IO があり、スリープ メソッドがない場合、これらの関数はどのように (そしてなぜ) 実装されますか? 同じメカニズムを使用して他の同期関数を実装できますか?
fs.readFileSync()
実際には単なるラッパーです
fs.readSync()
関数。したがって、問題は fs.read() と比較して fs.readSync() がどのように実装されているかです。これら 2 つの関数の実装を見ると、どちらも bindings モジュールを利用しています。この場合、どのように初期化されますか
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++ 呼び出しのオーバーロードを提供します。1 つはコールバックを受け取り、もう 1 つは受け取りません。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 を使用していることに注意してください。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_);
}
この関数は、このイベントの実行を処理するために新しい v8 スコープ オブジェクトを作成していることに注意してください。これは、非同期処理の非同期部分が発生する場所です。v8 エンジンは、この特定の呼び出しを個別に処理するために、新しい JavaScript インタープリター環境を起動します。つまり、ノードの独自のバージョンを構築/変更しないと、ノードと同じように、独自の非同期/同期バージョンの呼び出しを実装することはできません。そうは言っても、非同期は実際には I/O 操作にのみ適用されます。おそらく、物事をより同期させる必要があると考える理由の説明が適切でしょう。一般に、ノードがやりたいことをサポートしていないと思われる場合は、コールバック メカニズムを最大限に活用していないだけです。
そうは言っても、非同期動作が必要な場合は、イベント ノード モジュールを使用して独自のイベント ハンドラーを実装することを検討できます。また、どうしても同期的に実行する必要がある場合は、ネイティブ拡張を検討できますが、これは強くお勧めしません。非同期イベント ループ内で作業を行う方法を検討して、この方法で行う必要があることを取得します。この考え方を受け入れるか、別の言語に切り替えてください。
言語が処理したくない方法で処理するように言語に強制することは、悪いコードを作成するための優れた方法です。