readableイベントのコールバック関数で実行する res.read();nullが返ってくるときがあります


ネットを見てると

https.get(url, function(res){ var body = ''; res.setEncoding('utf8'); res.on('readable', function(){ body += res.read(); }); res.on('end', function(res){ cb(body); }); });

変数名とか違うかもですが こんなコードをよく見かけます

何も考えずにこれをコピペして使ってたんですが これだとちゃんと動かない時があります
問題なのはここ

res.on('readable', function(){ body += res.read(); });

res.read() で返ってくるのは文字列だけではなくnullが返ってくることもあります

http.get でJSONを受け取ってJSON.parse()にかけるプログラムを書いて動かすとJSONは正しいはずなのにエラーが出ていて 調べてみると パースしようとしている文字列の最後に null がついていました
res.read() で null を受け取っていてそれを文字列に結合するので {"a":1,....}null という文字をパースしようとしてたわけです

ただこれは絶対に起こるわけじゃないようです
必ず起きてたら ↑の方のコードがサンプルとして出回ってないですよね

数バイト程度のデータで↑のコードで動くことを確認してちゃんと動いたので 実際に使いたい数百KB~数MBのデータでやってみたら null が最後についていました
ある程度大きなファイルになってchunk分割が起きると(もしくは分割数が多いと)起きるのかもしれないです

こういう場合もあるので

https.get(url, function(res){ var body = ''; res.setEncoding('utf8'); res.on('readable', function(){ body += res.read() || ''; }); res.on('end', function(res){ cb(body); }); });

こう書くようにしましょう
null などfalseになる値が来ても それは無視したいので空文字にしてます

stream1の頃の dataイベント だと  データが届いたよ  と関数を実行してくれるんですが stream2 の readableイベント では 読み込みできるよ って関数実行してくれるだけなので read() でデータ取ってきてもデータがあるとは限らないんですね


確認したバージョンはiojs 3.3.1です
nodejsと言っておきながら実はiojsだったりします