I want to show my program's progress (exemplified here as a loop) on a website in real-time.
If I have a proper Ruby server running, a client should be able to connect to the server via websockets using a browser, and, once connected, receive 'updates' about a newly spawned process in real time. No polling, or Ajax required.
My idea to test this is to establish duplex communication between EventMachine::WebSocket and a client browser using clientside JavaScript. Build a loop, and send updates to a browser client.
ruby_socket_server.rb:
require 'eventmachine'
require 'em-websocket'
@sockets = []
EventMachine.run do
EventMachine::WebSocket.start(:host =>'localhost',:port =>8887) do |socket|
socket.onopen do
@sockets << socket
(0..10).each do |i|
puts "i am on loop #{i}" #to terminal window
@sockets.each do |s|
s.send "Just received notification of loop #{i}"
sleep(1) #pause for a second
end
end
end
end
main.js:
socket = new WebSocket('ws://localhost:8887');
socket.onopen = function(){
console.log('OPEN');
}
socket.onclose = function(){
console.log('CLOSED');
}
socket.onmessage = function(msg){
console.log(msg);
}
I expect it to look like:
What is actually happening:
I am unable to send data to the client from WITHIN a Ruby loop, i.e. it eventually sends all packets, but not DURING every iteration as expected. Instead, it waits until the server loop is complete, and sends multiple calls back to back.
What am I doing wrong here? Is what I am looking for even possible? Any thoughts on how to crack this nut, would be helpful.
EventMachine is not multithreaded. So if you do not let it run, i.e. if you stay inside any of its callbacks, then nothing will happen.
In your example you call sleep
inside a callback. This will prevent the callback from exiting and hence EM can't run, and if it can't run it can't send your message.
So any EM server has to be constructed fully around an evented callback setup.
That's why it's called Event Machine. It requires a slightly different way of thinking, but once you get the hang of it you should be fine.
Here's an example on how you could make it work:
EM.run do
EM::WebSocket.start(:host =>'localhost',:port =>8887) do |ws|
ws.onopen { |handshake|
puts "New connection from #{handshake.origin}"
loop = 1
timer = EM.add_periodic_timer(1) {
ws.send "Timer loop #{loop}"
if (loop += 1) == 5
EM.cancel_timer(timer)
ws.send "Bye"
ws.close_websocket
end
}
}
end
end