Node.jsのSync関数について
はじめに
Node.jsにはfs.readFileSync
のようにI/O系のコアモジュールにSync関数が用意されているものがあります。(一部の外部ライブラリでもSync関数を別途用意してある場合がある。)
こういったSync関数はブロッキングI/Oについて理解してないと、パフォーマンスの低下など思わぬ弊害が発生してしまうことがあります。
ブロッキングI/OとノンブロッキングI/O
簡単に言えば、ブロッキングI/Oは同期処理でノンブロッキングI/Oは非同期処理になります。同期処理は処理が完了しない限り次の処理が実行されることはありません。対して非同期処理は処理の完了を待たずに次の処理を実行することができます。
このコードはブロッキングI/Oの例です。
const fs = require('fs');
const data = fs.readFileSync('file.txt');
nextFunc();
readFileSync
の処理が完了しない限り、nextFunc
が実行されることはありません。
そして次はノンブロッキングI/Oの例です。
const fs = require('fs');
fs.readFile('file.txt', (err, data) => {
if (err) throw err;
});
nextFunc();
readFile
の処理の完了を待たずとも、nextFunc
が実行されます。
ブロッキングI/Oの問題点
Node.jsでプログラムの実行はシングルスレッドで行われるため、ブロッキングI/OではI/Oの結果に関わらず行うべき処理がある場合にも、その処理を実行することができません。
例えば、I/Oに50msの時間がかかる場合に、ブロッキングI/Oを行ってしまうと、その50msはプログラムの実行にCPUを充てることができず完全に待ちの状態となってしまいます。
したがって、Node.jsでHTTPサーバなど多数のリクエストを処理する場合にブロッキングI/Oを使用してしまうと、I/O中は他のリクエストを処理できなくなるため、パフォーマンスが著しく悪化することが考えられます。
さいごに
Node.jsのSync関数は、結果がそのまま返り値になるので一見便利なように見えますが(特にコールバック関数に苦手意識を持っている人は使いがち?)、思わぬ落とし穴があります。Sync関数を取り扱うときは十分注意して使用しましょう。
Discussion