LoginSignup
0
0

More than 1 year has passed since last update.

fs には promis 版もあるよ

Posted at

生成した大量のデータをファイルに書き出したい

「POSデータ処理のテストデータ作ってほしいんだよ。性能測るから1年分。ここに日付けいれてあとはフォーマットあってれば中身何でもいいから適当に作って。DBに食わせるためのCSVだから、ファイルは分けても分けなくてもいいよ。」

『はーい。適当に作りまーす。』


『さくっとNodeで出すかー。』

『んーっと、500バイトぐらいのデータが1日1000件ぐらい……せめて月単位ぐらいで分けるか……』

main.js
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

『しかも作成されたファイル全部中身空っぽやんけ。』
:thinking:

『あー、はいはい。 fs.writeFileSync の処理が終わる前に次のループ処理が走ってるのね。そらメモリぱんぱんになるわ。1行ずつ出力するかー。』

main.js
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

:thinking:

同じ結果になるのは当たり前で、つまるところJavascriptは基本的に関数の終了を待たずに次の処理に行ってしまう(語弊あり)から fs.appendFileSync のファイルへの書き込みが終わってないのに次々にデータを生成しまくってメモリがやばい。

fs.promises というのがある

昔だったらコールバックで対応していたんだろうけど今はちゃんと fs.promises っていうのがあって、プロミスを返してくれる関数群が存在する。

本来なら for await 使うかちゃんとpromiseの配列作って Promise.all() でも叩くべきなのだが、めんどくさいので await 使ってしまった。

main.js
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}月のファイルを出力しました`)
}})()

めでたしめでたし。

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0