Cafe capybara TECH-BLOG

PHPな会社でゴリゴリしてるあかいいぬの技術ブログです

async.js を使ってみる

callback 地獄に陥りやすい javascript さんを救うべくして現れた async.js 。
機能の解説や使い方は他の方が詳しくやっていると思うので、

あたりをご覧いただければと。


で、今回は自分で使ってて「ん?」と思った部分を紹介。

async = require 'async'
l = console.log

async.series [
  (callback) ->
    l 'Series 1'
    callback null, 1
  (callback) ->
    l 'Series 2'
    callback null, 2
  (callback) ->
    l 'Series 3'
    callback null, 3
], (err, results) ->
  throw err if err?
  l 'Finished ' + JSON.stringify results

# * prints
# Series 1
# Series 2
# Series 3
# Finished [1,2,3]

簡単

async = require 'async'
l = console.log

someMethod = ->
  async.waterfall [
    (callback) ->
      len = 1
      l 'waterfall ' + len
      callback null, len + 1
    (len, callback) ->
      l 'waterfall ' + len
      callback null, len + 1
    (len, callback) ->
      l 'waterfall ' + len
      callback null, len + 1
  ], (err, results) ->
    throw err if err?
    l 'waterfall finished ' + results

async.series [
  (callback) ->
    l 'series 1'
    someMethod()
    callback null, 1
  , (callback) ->
    l 'series 2'
    callback null, 2
], (err, results) ->
  throw err if err?
  l 'series finished ' + JSON.stringify results

# * ideal prints
# series 1
# waterfall 1
# waterfall 2
# waterfall 3
# waterfall finished 4
# series 2
# series finished [1,2]

# * real prints
# series 1
# series 2
# series finished [1,2]
# waterfall 1
# waterfall 2
# waterfall 3
# waterfall finished 4

アルェ?

というのも、 async.series さんは同期的に、 async.waterfall さんはどうやら非同期に事を済ますらしく、 someMethod で何か処理をしてから次の series に行きたいなって時でも、 waterfall さんはよろしくない動きをしてしまう。

中身を読むと、どうやらwaterfallの各メソッドは setImmediate を使って後からやってねって形で呼ばれているみたいなので、仕方ない。

# ...
someMethod = (parentCallback) ->
# ...
  ], (err, results) ->
    l 'waterfall finished ' + results
    parentCallback err, 1 if typeof parentCallback is 'function'

async.series [
  (callback) ->
    l 'series 1'
    someMethod callback
  , (callback) ->
# ...

# * prints
# series 1
# waterfall 1
# waterfall 2
# waterfall 3
# waterfall finished 4
# series 2
# series finished [1,2]

という風に親元の callback を引数で渡してあげて、 waterfall の最終処理の callback 内で呼んであげるという手法で一応予想通りの順番で動いたわけだが、どうも納得がいかない...。もっとうまいことできないかな?

そもそも状態が残りまくっててキモイので理想的な書き方からは程遠いはずです。なので、「seriesとwaterfallには同期と非同期の差があるんだな」くらいの感覚でとらえてください。