-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
'invalid state' error sourcing websocket script #40
Comments
This is something that we haven't really documented yet, so it's totally understandable that you're running into problems. Here's a modified version of your example where the ws2 <- websocket::WebSocket$new("ws://echo.websocket.org", autoConnect = FALSE)
ws2$onMessage(function(event) { message("Message received: ", event$data)})
ws2$connect()
Sys.sleep(2)
ws2$send("hello")
#> Error in wsSend(private$wsObj, msg): invalid state The code that handles making the connection doesn't run immediately when you call The queue does not get processed when Another issue is that establishing the connection takes some time, so even after the connection event is started, some time needs to elapse before it's actually connected. Here's a version of the code that will work in a script or function: # Wait up to 5 seconds for websocket connection to be open.
poll_until_connected <- function(ws, timeout = 5) {
connected <- FALSE
end <- Sys.time() + timeout
while (!connected && Sys.time() < end) {
# Need to run the event loop for websocket to complete connection.
later::run_now(0.1)
ready_state <- ws$readyState()
if (ready_state == 0L) {
# 0 means we're still trying to connect.
# For debugging, indicate how many times we've done this.
cat(".")
} else if (ready_state == 1L) {
connected <- TRUE
} else {
break
}
}
if (!connected) {
stop("Unable to establish websocket connection.")
}
}
ws2 <- websocket::WebSocket$new("ws://echo.websocket.org", autoConnect = FALSE)
ws2$onMessage(function(event) { message("Message received: ", event$data)})
ws2$connect()
poll_until_connected(ws2)
ws2$send("hello") |
Super helpful and insightful- thank you. As a practical matter, in a script, is that how you would set up the handlers, ie, you wouldn't autoconnect, then you'd define the handlers, and then you'd invoke poll_until_connected? |
Glad it was helpful! In a script or function, you don't have to create with Also note that if you're doing this from an R script that you're running from the command line (not the R console, but a shell) , you will need to call One more thing: if you're doing this from inside a Shiny app, calling ws <- websocket::WebSocket$new("ws://echo.websocket.org", autoConnect = FALSE)
ws$onMessage(function(event) { message("Message received: ", event$data)})
ws$onOpen(function(event) { ws$send("hello") }
ws$connect() I'm reopening this issue to remind us to document this. |
Got it. Next stop, later:: Thank you Winston. |
For future reference, here's a slightly cleaner way to do it with promises. library(promises)
run_child_loop_until_resolved <- function(p) {
# Chain another promise that sets a flag when p is resolved.
p_is_resolved <- FALSE
p <- then(p, function(value) p_is_resolved <<- TRUE)
err <- NULL
catch(p, function(e) err <<- e)
while (!p_is_resolved && is.null(err) && !loop_empty()) {
run_now()
}
if (!is.null(err))
stop(err)
}
ws <- WebSocket$new("ws://echo.websocket.org", autoConnect = FALSE)
ws$onMessage(function(event) { message("Message received: ", event$data)})
p <- promise(function(resolve, reject) {
ws$onOpen(resolve)
# Allow up to 10 seconds to connect to browser.
later(function() {
reject(paste0("Chromote: timed out waiting for WebSocket connection to browser."))
}, 10)
})
ws$connect()
run_child_loop_until_resolved(p)
ws$send("hello") This pattern, using the Note that this can potentially have unsafe interactions with Shiny and other code that uses the later package, at least until we merge in r-lib/later#84. Note 2: using this pattern for timeouts can lead to memory being held longer than necessary. The rejection function scheduled with |
I'm closing this because I see the original issue as resolved, but I encourage any participant to re-open or open another issue if they feel otherwise. |
I've been working on the package for a couple of days now, but I was still surprised by this behavior. I thought I was doing something wrong until I found this issue. I would expect either:
Having an asynchronous function that requires some I'll reopen just to kick off the conversation. |
An update: we have worked out most of the technical issues for making async code work synchronously, so the main issue now is deciding on what kind of interface to provide for it. |
Hello there, I'm wondering if this issue has any update? FYI: I'm running a script in a Linux server, form the terminal ($ Rscript script_name.R), that needs to open a connection, send a message and close it during a loop. It was great to get the above |
this may be related to this issue filed yesterday and closed today, though I'm still experiencing a similar problem even after having updated the websocket package (my sha matches today's release: ccc03d5).
In short, the following works fine stepping through at the console, but it fails if one
source
's the script, and the reprex output below reflects this.I'm very ignorant about how websockets work but am very grateful for this R package-- thank you to the developers/ maintainers who have made it availble.
Created on 2019-02-05 by the reprex package (v0.2.1)
Session info
Finally, and separately: one thing I haven't attempted is registering the handlers in the same function call, in part because I don't know how to do this: Any example code would be helpful in the readme vignette.
The text was updated successfully, but these errors were encountered: