◆ drain イベントを待たずに write してもちゃんと書き込めてる
◆ ただし書き込めない以上 待機中もデータを保持しておかないといけないのでメモリを使う
◆ 書き込めない間は新しくデータを取得したり作ったりしないことでメモリを効率的に使える
◆ すでに変数に持ってたり能動的に取得するのではなく受動的に取得する場合は自分で変数に保持することになるので一緒
  ◆ 自分で管理するか write 先が自動で管理してくれるかなので write してしまったほうが楽

普段 Node.js で stream を使うときってたいていライブラリ間の pipe をするくらいで複雑なことはしません
なので Node.js の stream の v1,v2,v3 (だっけ?) の変遷とかあまり詳しくないですし たまに調べてもすぐ忘れてます

今回はそんな stream で時々見かける drain イベント これがよくわかってなかったので調べてみました

drain イベントは書き込みができるようになったら起きるイベントで これが起きたら書き込みをするという例を見かけます
ただ それだと drain 無視して書き込んだらどうなるの?とかこの辺がよくわかってません
書き込みできないんだからエラーが出たり無視されて何も起きなかったりするのでしょうか

書き込んで見る

書き込めるようになったことは drain イベントでわかりますが 書き込めなくなったことは write メソッドで書き込んだ後の返り値でわかるようです
返り値は表示するだけで 無視して書き込み続けます
書き込む先はファイルにしました

const fs = require("fs")
const stream = fs.createWriteStream("file")

for (let i = 0; i < 100000; i++) {
const ok = stream.write("A")
if (!ok) console.log(i)
}

結果は 16383 ~ 99999 までが出力されました
非同期にしてなくて同期処理なので for のループ中に実際のファイルへの書き込み処理は全く実行されず いったん書き込みが可能になるということはなく一度出力されてからはずっと出力され続けます
適当なところで setTimeout を await すれば 待ち時間中に書き込みが終わって drain イベントが起きてそこからしばらくは書き込み可能なので出力がでなくなるはずです

書き込みはループ 1 回に 1 byte です
最初に出たのが 16383 なので 16384 回目の書き込みでいっぱいになって書き込めなくなったということです
16384/1024=16 なので 16 KiB が限度のようです

実際に書き込んだファイルを見てみるとちゃんと 100000 byte 全部書き込めていました
drain イベントを気にせず書き込んでも問題はないようです

drain の使いみち

drain を待たなくてもいいなら drain なんて気にしなくてもいいと思えますが 使いみちは効率よくするためです
write メソッドで書き込んでも実際のファイルへの書き込みが進んでないわけですから write メソッドをいっぱい実行しても書き込みができるようになるまでそのデータを保持しておく必要があり どこかの変数上で保持されます
つまり使用メモリが増えます

今回のようなループだとループを回さないことにはデータは増えないので 書き込みができるようになるまで待機していれば不要なメモリは確保しなくて済みます
大きなファイルをストリームで読み書きしてコピーする場合 書き込みが追いついていないのにどんどんファイルを読み取るとメモリ不足でクラッシュするかもしれません
なので 自分でストリーム制御する場合は drain イベントを意識しておいたほうが良いです

ただ 送るデータを新しく作ったり取得するならそうなのですが すでに全部変数上に持ってるとか 一方的に送られて来て止めることができない場合はメモリの節約はできませんし それならまとめて write してしまったほうが drain イベントを受けてリスナで処理してという分も減らせて効率良くなると思います