0

I want to allow JavaScript to register events, with a syntax like

object.on('eventName', (event) => {
  event.something();
});

The event object provides different methods depending on what 'eventName' is (depending on what type of event I'd like to add a handler for).

I will need to create host objects for object and for event, I'm assuming the class needed for object will have to look something like

public class Events {
  public void on(String eventName, X<FirstEvent> event) {
    // ...
  }

  public void on(String eventName, X<SecondEvent> event) {
    // ...
  }
}

If there are two types of events.

But I don't know how to get graalvm to automatically choose the correct on method, and I don't know what type to use for the second argument (it has to be a JavaScript anonymous function which accepts a single argument of a certain type, and must be runnable later by my Java code).

How can I store JavaScript anonymous function

(event) => {
  event.something();
}

as some kind of Java Runnable, and make sure GraalVM knows which on method to use depending on the value of the first string argument (because the event objects passed will be different for different events).

I think this may be a super complex question, I'm finding it tricky to phrase properly.

I'd like to create a way for JavaScript to register event handlers that are functions that will be called at later times with certain arguments.

Alan Bateman
  • 5,283
  • 1
  • 20
  • 25
theonlygusti
  • 11,032
  • 11
  • 64
  • 119
  • Do I understand correctly that you would like to have overloaded `on` methods and have GraalVM automatically pick the correct one based on the *value* of the `eventName`? Because the second argument to `on` have the type: `Consumer`? – Oleg Šelajev Feb 08 '19 at 08:38

1 Answers1

1

I'm not 100% sure if this is what the question is asking, but I hope this helps.

In order for this snippet to work:

object.on('eventName', (event) => {
  event.something();
});

The simplest Java class that would work for the interoperability purposes:

public class MyJavaClass { 
  public void on(String type, Consumer<Object> handler) {
  }
}

Then you can use if from JavaScript like:

const myJavaClassType = Java.type('MyJavaClass')
var object = new myJavaClassType();

object.on('X', (event) => { console.log(event) })

This would work, and the method .on() would run and you can store the handler to invoke at some later time.

In Java there's no method overloading based on the value of the parameters. So if you want to invoke the specific handler based on the value of the eventName you need to write it yourself.

Something like this perhaps:

public class MyJavaClass { 
  private Map<String, Consumer<Object>> handlers = new HashMap<>();

  public void fire(String type, Object event) { 
    Consumer<Object> handler = handlers.get(type);
    if(handler != null) { 
        handler.accept(event);
    }
  }

  public void on(String type, Consumer<Object> handler) {
      handlers.put(type, handler);
  }
Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25
  • I'm not sure whether or not this helped him, but it helped me. I am converting from Nashorn to graal.js for user-defined extensions to my kotlin/java program. I was handling callbacks like this with something called ScriptObjectMirror (that is part of Nashorn) and I was trying to find the graal.js way to do it. Your answer clued me in that I just need to go back to java.util.function. I changed my @FunctionalInterface to a Consumer and it works. That was the only change I had to make to move from Nashorn to embedded graal.js -- Yay! Thanks for your answer. – wz2b Nov 21 '19 at 22:24