puppeteer-rubyっていうライブラリを作っているときに、WebSocketのコールバック順序がぐちゃぐちゃになる事象に嫌気がさして、WebSocketの代替実装を探していたところ、
(ぜんぜんWebSocketではないがw)asyncという面白そうなライブラリを見つけたのでメモ。
複数のタスクを待ち合わせる
require 'async'
def do_task(name)
Async do |task|
puts "start task - #{name}"
task.sleep(name.to_i)
puts "end task - #{name}"
name
end
end
def all_task(*args)
Async do |task|
puts "start tasks - #{args}"
values = args.map do |arg|
do_task(arg)
end
puts "wait for tasks....."
result = values.map(&:wait)
puts "end tasks - #{result}"
result
end
end
all_task(1,2,3,4,5) を実行すると
start tasks - [1, 2, 3, 4, 5]
start task - 1
start task - 2
start task - 3
start task - 4
start task - 5
wait for tasks..... # ここまで一瞬
end task - 1
end task - 2
end task - 3
end task - 4
end task - 5 # ここまで、1秒毎に
end tasks - [1, 2, 3, 4, 5] # ↑と同時
JSでいうところの Promise.all
(せんぶまつ)はできるが、
Promise.race
(どれか1つが終わるまで待つ)ができるかはまだ確かめていない。
あと、トップレベルでの実行(Async do ... endをしない状態)では、並列実行にならずタスクは直列実行された。
Async do ... end
内に限り並列実行されるようだ。
Async::Queue
Promiseのように、誰かがresolveしてくれるまで待ち受けるようにしたいときは、Queueを使うとよさそう?
require 'async'
require 'async/queue'
def resolvable
Async do |task|
q = Async::LimitedQueue.new
Async do
puts "q start"
result = q.dequeue
puts "q end - #{result}"
end
task.sleep 3
q << 3
end
end
これも、外側の Async ... end
を忘れると、内側の Async...do
を待ち受けようとするため、このブロックが抜けられず、task.sleepまでたどり着かない。
ドキュメンテーションが若干弱いいけど、重厚なconcurrent-rubyに比べると単機能で手軽に使えて良さそう。