vert.xscala.jsscalajs-react

Vertx Eventbus client with Scala.js


I'm trying to create a new Vertx Eventbus instance in Scala.js project. No matter what I do, I get INVALID_STATE_ERR when trying to register handler in onopen function.

It seems like the onopen is being fired prematurely.

Example code: Eventbus.scala:

@js.native
@JSImport("vertx3-eventbus-client",JSImport.Default)
class Eventbus(url:String) extends js.Any{

def registerHandler(address:String,callback:
(js.Dynamic,js.Dynamic)=>Unit): Nothing =js.native

var onopen:Unit=js.native

}

Then, from my app, I call it:

    val eb = new Eventbus("http://localhost:8080/eventbus")
    eb.onopen={
    println("opening")
    eb.registerHandler("activity-updates", (err, mess) => {
      val message = mess.body.toString
      println(message)
    })
  }

Solution

  • Your typing of onopen is wrong. It should probably be js.Function0[Unit].

    What happens is that the code in onopen is executed immediately (rather than "on open").

    Change to this:

    var onopen: js.Function0[Unit]=js.native
    
    eb.onopen={ () =>
    println("opening")
    eb.registerHandler("activity-updates", (err, mess) => {
      val message = mess.body.toString
      println(message)
    })
    

    The reason registerHandler got executed immediately is that Scala (in general) allows statements (and therefore blocks) in expression position.

    So if we just look at the assignment to ep.onopen:

    eb.onopen={
      println("opening")
      eb.registerHandler("activity-updates", (err, mess) => {
        val message = mess.body.toString
        println(message)
      })
    }
    

    This assigns the result of the block on the right hand side (of value Unit) to eb.onopen. Practically this means that statements get executed like this:

    println("opening")
    val tmp = eb.registerHandler("activity-updates", (err, mess) => {
      val message = mess.body.toString
      println(message)
    })
    
    eb.onopen=tmp
    

    So you can see that registerHandler gets executed even before onopen is even assigned. This feature (statements in expression position) is of course rarely useful for things returning Unit. However, there are cases where it is extremely useful. For example, temporaries:

    val x = {
      val myHelper = ???
      if (myHelper) ???
      else ???
    }
    

    Now myHelper does not escape in scope and it is clear that it was only useful to calculate x and not used anymore later.