Pythonには進捗を簡単に表示するためのtqdmという有名なモジュールがあります。
nodeだとどうも使い勝手の良いものが無かったので書いてみました。
for ~ of構文
を使う時にオブジェクトをnqdm()でラップすることで簡単にプログレスバーを表示することが出来ます。
また、他のループにも対応できるように手動で進捗を更新する機能もついています。
開発に利用したnodeのバージョンはv10.10.0
です。
使い方
インストール
npm install nqdm
使用例
const nqdm = require('nqdm')
// ラップするだけのパターン
const arr = [...Array(1000)]
for(const v of nqdm(arr)) {
// ここで何かする
}
// 手動での更新も可能
const bar = nqdm(arr.length)
arr.forEach(v => {
bar.process()
})
↓
4.70% [==> ] 00:00:04 00:01:37 [19.72 iter/sec]
こんな感じで進捗、経過時間、残り時間、1秒あたりの処理数を自動的に表示します。
オプション
出力先やコールバックなどを指定することができ、Generatorにも対応しています。
詳細はGithubのリポジトリに書いておきました。
Yuhsak/nqdm: Simple progress indicator for node.js
中身の話
ECMAScript 2015にはiterable(反復可能)オブジェクトという概念があります。
具体的には組み込みのオブジェクトだとString、Array、TypedArray、Map、Setが該当するのですが、ざっくり言うとfor~of文で回せるオブジェクトですね。
iterableなオブジェクトは自分で作ることも出来て、任意のオブジェクトの[Symbol.iterator]プロパティに「nextメソッドを実装したiteratorを返す関数」を実装すればOKです。
何を言っているのかわからないよ!という方は上記の解説が非常に詳しく書いてあって分かりやすいので読んでみてください。
nqdmは渡されたオブジェクトからiteratorを取得し「自身のイテレーション時に渡されたオブジェクトをイテレートした値を返す」新たなオブジェクトを生成して返します。
こうして生成したオブジェクトをはさんでラップすることでfor~ofによるイテレーションをフックして進捗の更新を行っています。
簡略化したコードはこんな感じです。だいたい雰囲気つかめるでしょうか。
const wrapped = (iterable) => {
const iterator = entity[Symbol.iterator]()
let i = 0
const wrapperIterator = {
next: () => {
// ここにイテレーション時にフックしたい処理を書く
i = i+1
console.log(`${i} times iterated.`)
return iterator.next()
}
}
const wrapperIterable = {
[Symbol.iterator]: () => wrapperIterator
}
return wrapperIterable
}
自分の環境ではとても便利に使えています。
オブジェクトをnqdm()
でラップするやり方はfor~in構文`には対応していないので注意して下さい。
for~inを使う時は反復可能プロトコルは利用されずにオブジェクトの持つlengthプロパティを参照しているようなので、今回の実装では対応していません。.process()
を呼んで手動で更新する必要があります。
もしうまく動かなかったりしたら僕に教えてくれるかプルリク飛ばしてもらえると嬉しいです。