LoginSignup
0
0

More than 3 years have passed since last update.

fs.stat を Promise 化して複数のファイルの stat を一気に取る。

Last updated at Posted at 2019-12-09

書いてから思ったのですが、下から読んだ方がいいかも。

Promise 以前の状況

例えば node.js で 'x' という名前のファイルの stat を取得したい場合

sample.js
const fs = require( 'fs' )

fs.stat(
    'x'
,   ( er, stat ) => {
        if ( er )   console.error( er )
        else        console.log( stat )
    }
)

という風にやってたと思います。

ファイルが複数の場合

複数のファイル、例えば 'x', 'y' という名前のファイルの stat を取得してから何かやりたいような場合、時間のかかる逐次処理でよければ

sample.js
const fs = require( 'fs' )

const
stats = []
fs.stat(
    'x'
,   ( er, stat ) => {
        if ( er )   console.error( er )
        else {
            stats.push( stat )
            fs.stat(
                'y'
            ,   ( er, stat ) => {
                    if ( er )   console.error( er )
                    else {
                        stats.push( stat )
                        console.log( stats )
                    }
                }
            )
        }
    }
)

並行にしたい場合はちょっと苦しいテクニックを使って

sample.js
const fs = require( 'fs' )

const
stats = [ null, null ]
fs.stat(
    'x'
,   ( er, stat ) => {
        if ( er )   console.error( er )
        else {
            stats[ 0 ] = stat
            if ( stats[ 1 ] ) console.log( stats )
        }
    }
)
fs.stat(
    'y'
,   ( er, stat ) => {
        if ( er )   console.error( er )
        else {
            stats[ 1 ] = stat
            if ( stats[ 0 ] ) console.log( stats )
        }
    }
)

のようにする必要がありました。ちょっと大変です。特にファイルの数が増えたりしたら。

Promise 以降

fs.stat を Promise 化すると上のような大変さがなくなります。

Promise 化

util.promisify を使う
sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

promisify( fs.stat )( 'x' ).then(
    _ => console.log( _ )
).catch(
    _ => console.error( _ )
)
素でやる

util.promisify がある今となってはもうやることはないと思いますが、参考までに。

sample.js
const fs = require( 'fs' )

new Promise(
    ( rs, rj ) => fs.stat(
        'x'
    ,   ( er, stat ) => er ? rj( er ) : rs( stat )
    )
).then(
    _ => console.log( _ )
).catch(
    _ => console.error( _ )
)

ファイルが複数の場合

Promise.all を使う

Promise の配列を作って Promise.all に渡してやります。配列の中のすべての Promise が解決されるか、どれかがリジェクトされるまで待ちます。

sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

Promise.all(
    [ 'x', 'y' ].map(
        _ => promisify( fs.stat )( _ )
    )
).then(
    _ => console.log( _ )
).catch(
    _ => console.error( _ )
)
関数化してみる
sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

const
Stats = files => Promise.all( files.map( _ => promisify( fs.stat )( _ ) ) )

Stats( [ 'x', 'y', 'z' ] ).then(
    _ => console.log( _ )
).catch(
    _ => console.error( _ )
)

最後に

Promise.all を使わない手はありませんね!

0
0
0

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