生成した大量のデータをファイルに書き出したい
「POSデータ処理のテストデータ作ってほしいんだよ。性能測るから1年分。ここに日付けいれてあとはフォーマットあってれば中身何でもいいから適当に作って。DBに食わせるためのCSVだから、ファイルは分けても分けなくてもいいよ。」
『はーい。適当に作りまーす。』
『さくっとNodeで出すかー。』
『んーっと、500バイトぐらいのデータが1日1000件ぐらい……せめて月単位ぐらいで分けるか……』
const fs = require('fs')
const date = new Date(2017, 0, 1)
const dir = 'hoge'
while(date.getFullYear() < 2018 ){
const month = date.getMonth()
while(date.getMonth === month){
const file = `${dir}/data_${month+1}.csv`
let lines = []
for(let l=1; l <= 1000 ; l++) {
lines.push(csvのデータ1行だけ出すやつ(date))
}
fs.writeFileSync(file, lines.map(l=>l.join(',')).join('\n'), ()=>{} )
date.setDate(date.getDate()+1);
}
console.log(`${month+1}月のファイルを出力しました`)
}
『ぽちっとな。』
> node main.js
1月のファイルを出力しました
2月のファイルを出力しました
3月のファイルを出力しました
4月のファイルを出力しました
5月のファイルを出力しました
6月のファイルを出力しました
7月のファイルを出力しました
(めっちゃエラーメッセージ)
JavaScript heap out of memory
『しかも作成されたファイル全部中身空っぽやんけ。』
『あー、はいはい。 fs.writeFileSync
の処理が終わる前に次のループ処理が走ってるのね。そらメモリぱんぱんになるわ。1行ずつ出力するかー。』
const fs = require('fs')
const date = new Date(2017, 0, 1)
const dir = 'hoge'
while(date.getFullYear() < 2018 ){
const month = date.getMonth()
while(date.getMonth === month){
const file = `${dir}/data_${month+1}.csv`
for(let l=1; l <= 1000 ; l++) {
line = csvのデータ1行だけ出すやつ(date)
fs.appendFileSync(file, line.join(','), ()=>{} )
fs.appendFileSync(file, '\n', ()=>{} )
}
date.setDate(date.getDate()+1);
}
console.log(`${month+1}月のファイルを出力しました`)
}
『ぽちっとな。』
> node main.js
1月のファイルを出力しました
2月のファイルを出力しました
3月のファイルを出力しました
4月のファイルを出力しました
5月のファイルを出力しました
6月のファイルを出力しました
7月のファイルを出力しました
(めっちゃエラーメッセージ)
JavaScript heap out of memory
同じ結果になるのは当たり前で、つまるところJavascriptは基本的に関数の終了を待たずに次の処理に行ってしまう(語弊あり)から fs.appendFileSync
のファイルへの書き込みが終わってないのに次々にデータを生成しまくってメモリがやばい。
fs.promises というのがある
昔だったらコールバックで対応していたんだろうけど今はちゃんと fs.promises っていうのがあって、プロミスを返してくれる関数群が存在する。
本来なら for await
使うかちゃんとpromiseの配列作って Promise.all()
でも叩くべきなのだが、めんどくさいので await
使ってしまった。
const fs = require('fs').promises
const date = new Date(2017, 0, 1)
const dir = 'hoge'
;(async () => {
while(date.getFullYear() < 2018 ){
const month = date.getMonth()
while(date.getMonth === month){
const file = `${dir}/data_${month+1}.csv`
for(let l=1; l <= 1000 ; l++) {
line = csvのデータ1行だけ出すやつ(date)
await fs.appendFile(file, line.join(','))
await fs.appendFile(file, '\n')
}
date.setDate(date.getDate()+1);
}
console.log(`${month+1}月のファイルを出力しました`)
}})()
めでたしめでたし。