Getting Results from Plugin.notifyListeners

I have a Capacitor plugin (Android) that has a method named “flyToVenus” to perform some complex work. This plugin method eventually determines that it needs to call back to JavaScript for it to do some of the work. So the plugin method then calls back to JavaScript with “notifyListeners” to force JavaScript to perform its work. The tricky part is that I need the native plugin code to wait for JavaScript to finish its work before continuing in the native code.

An obvious solution would be for JavaScript to do its work before calling the plugin, and then JavaScript could pass the results to the plugin up front. But this is impossible in my scenario for reasons too complicated to explain here.

@PluginMethod
public void flyToVenus(PluginCall call) {
    /* ... do some processing in the plugin ... */

    /* plugin decides it needs to call JavaScript so that it can perform some work */
    this.savedResult = -1;
    JSObject data = new JSObject();
    data.put("value1",42);
    data.put("value2",37);
    notifyListeners("addTwoValues",data);

    /* now imagine that the JavaScript code receives the "addTwoValues" event and */
    /* computes the result of 79.  The JavaScript code then invokes another plugin */
    /* method named "provideResult" (shown below) and sends back the value 79.*/

    while (this.savedResult < 0) {
        try { Thread.sleep(10); } catch (InterruptedException ignored) {}
    }
    int result = this.savedResult;

    /* now do so more processing with the "result" value */
    call.resolve();
}

@PluginMethod
public void provideResult(PluginCall call) {
    this.savedResult = call.getInt("result");
    call.resolve();
}

Obviously the above Java plugin code does not work as intended-- it blocks forever in the while-loop. I think the problem is that when I loop and wait for JavaScript to call the “provideResult” plugin method, nothing can actually happen because JavaScript’s main thread is blocked by my own wait loop.

My question is, how can I simulate this behavior? Is there some call I can put into my while loop that will force the JavaScript main thread to execute the next microtask in its queue? Or some other way to invoke JavaScript code from a plugin method that is already running?

You definitely don’t want to sleep the main Java thread as you don’t want to lock it up. Can’t you run the “more processing” in the provideResult method after JS calls back saying continue?

You would want to kick it off on a different worker thread though so call.resolve() is called right away.

I guess you could also kick off the loop where you have it now but do it in a worker thread so call.resolve() happens right away not locking the main thread. One option is to use a Handler to schedule the check (like your while loop) in conjunction with the ExecutorService to spawn a new thread. I just did this setup in my audio plugin to have a native loop running in the background.

I create a main shared ExecutorService in the plugin here. I got this from Capacitor’s HTTP plugin.

If someone else has a better idea, please share. My Java experience is very limited…LOL.

Blockquote
Can’t you run the “more processing” in the provideResult method after JS calls back saying continue?

Unfortunately no-- this is just a contrived example and the real situation is much more complex than what is shown here. The whole idea is that I don’t want call.resolve (in the flyToVenus method) to execute until the “more processing” is finished. I want the code that called flyToVenus to block until the processing is completed, so that things happen in the correct order.

It’s actually even deeper than what I’ve shown here. In reality, the “provideResult” plugin method could itself call back to JavaScript (again) for yet more processing, and I would want the same behavior again. So the calls back and forth between the native plugin code and JavaScript could nest an arbitrary number of levels deep.

It sounds like what I want is probably not possible in the current Capacitor framework. It feels very limiting that the only interaction that the native code can have with JavaScript is send an asynchronous event.

I’d love to hear any suggestions on how to implement such arbitrary nested calls from native code to JavaScript code. Otherwise it seems like enhancing the Capacitor framework to provide a mechanism to synchronously call from native code to JavaScript would be a nice improvement.