fibersを使って同期しよう
walkとfibersのインストール
今回はnode.jsとCoffeeScriptで遊んでみましょう。
- [walk] GitHub - solderjs/node-walk: A semi-port of python's os.walk
- [fibers] GitHub - laverdet/node-fibers: Fiber/coroutine support for v8 and node.
walkとfibersをインストールします。
$ mkdir walk_fibers $ cd walk_fibres $ npm install walk fibers $
walkによる非同期ディレクトリ走査
walkは、非同期にディレクトリを再帰走査してファイルを処理するライブラリです。非同期なので速度を稼ぐことができるでしょう。
- walk.coffee
# node.js modules Path = require('path') # npm modules Walk = require('walk') class Traverse constructor: -> @traverse_async('.') traverse_async: (root_dir) => list = [] # *1_1: Start directory traverse. walk = Walk.walk(root_dir) # *1_2: Callback on file stat-ed. walk.on 'file', (root, stat, next) => filename = Path.join(root, stat.name) list.push(filename) next() # *1_3: Callback on walk ended. walk.on 'end', => list.sort() # *1_4: Do something with list *asyncronously* in the walk callback. console.log(list) new Traverse
walkの動作
まず*1_1でディレクトリの走査が始まります。
そして*1_2でファイルがstatされると呼び出されるコールバックを登録します。
statされるたびに*1_2が呼び出され、配列にファイルパスを追加していきます。
ディレクトリの走査が終了するときに呼び出されるのが*1_3のコールバックです。
ディレクトリの走査が終了したら配列をソートします。
そして*1_4でlistを得てコールバック内で出力します。
実行はこうなります。
$ coffee walk.coffee [ 'node_modules/fibers/.npmignore', 'node_modules/fibers/LICENSE', 'node_modules/fibers/README.md', 'node_modules/fibers/bin/.npmignore', 'node_modules/fibers/bin/darwin-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/darwin-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.6/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.14/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.6/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.14/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.6/fibers.node', ... 'walk.coffee', 'walk_fibers.coffee' ] $
fibersによる同期化
今度はfibersも使います。ディレクトリ走査の非同期処理を待ち受けることができます。
- walk_fibers.coffee
# node.js modules Path = require('path') # npm modules Walk = require('walk') Fiber = require('fibers') class Traverse constructor: -> # *2_1: Register @main_procedure as the main thread and run the main thread. Fiber(@main_procedure).run() main_procedure: () => list = @traverse('.') # *2_6: Do something with list *syncronously* in the main procedure. console.log(list) traverse: (root_dir) => list = [] # *2_2: Get the main thread for later at *2_4. main_thread = Fiber.current # Start directory traverse. walk = Walk.walk(root_dir) # Callback on file stat-ed. walk.on 'file', (root, stat, next) => filename = Path.join(root, stat.name) list.push(filename) next() # Callback on walk ended. walk.on 'end', => list.sort() # *2_4: Resume back the the main thread at *2_5. main_thread.run(list) # *2_3: Yield the main thread to async walk procedure list = Fiber.yield() # *2_5: Will be resumed here. return list new Traverse
fibersの動作
まず*2_1のところでmain_procedure()をメインスレッドとして登録して走らせます。
*2_2でメインスレッドのオブジェクトを取得します。*2_4でこのオブジェクトを利用するための下準備です。
travase()で非同期的にwalkを処理します。
非同期処理の間にメインスレッドに制御が戻りますが、*2_3でFiber.yield()することでメインスレッドの処理を明け渡し、非同期処理が終了するのを待ちます。
非同期処理が終わると*2_4でwalk終了関数からメインスレッドを復帰させます。
メインスレッドが同期され*2_5に移りtraverse()を抜けると、*2_6でmain_procedure()に戻り、listを使ってその後の処理を行うことができるようになります。
実行はこうなります。
$ coffee walk_fibers.coffee [ 'node_modules/fibers/.npmignore', 'node_modules/fibers/LICENSE', 'node_modules/fibers/README.md', 'node_modules/fibers/bin/.npmignore', 'node_modules/fibers/bin/darwin-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/darwin-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/darwin-x64-v8-3.6/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.14/fibers.node', 'node_modules/fibers/bin/linux-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/linux-x64-v8-3.6/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.11/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.14/fibers.node', 'node_modules/fibers/bin/win32-ia32-v8-3.6/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.11/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.14/fibers.node', 'node_modules/fibers/bin/win32-x64-v8-3.6/fibers.node', ... 'walk.coffee', 'walk_fibers.coffee' ] $
fibersの利点
main_procedure()でtraverse()を実行した後に*2_6以降の同じ階層内で更に処理を続けられるコードになっています。
walkの非同期処理の後の手続きをwalk終了コールバック内じゃなくて、メイン処理で行えるところが味噌です。