Skip to content

Can not resolve NodeJS Promise Concurrently from Java Side #817

Open
@pbk20191

Description

@pbk20191

It is clear that javascript is not supported under multi thread environment, but I suppose some components should handle multi-thread.

  1. Promise-Like
    Promise is good option to express concurrent control flow. However in current implementation we can not resolve Promise from different Thread!. (Except for few case)

(1) NodeJS -> Java

const JvmContextClass = Java.type("com.example.Executor")
const jvmContext = new JvmContextClass()
const myPromise = new Promise(jvmContext);
await myPromise
package com.example

class Executor {

    void then(Value resolve, Value reject) {
        Executors.newSingleThreadExecutor().execute(() => {
            try {
                Object someResult = computeSomething();
// Error, NodeJS is holding the context
                resolve.executeVoid(someResult);
            } catch (Throwable t) {
// Error, NodeJS runtime is holding the context
                reject.executeVoid(t);
            }
        })
// return control of current context back to NodeJS
    }

}

Only solution for this situation is to block the nodejs until Java resolve the Promise.

(2) Java -> embedded Javascript
This case is not a problem, since Java developer can synchronize the access to the Context

  1. NodeJS- microTasks
    We cannot queue runnable to the nodejs taskqueue e.g) setInterval, setTimeout, setImmediate when nodejs is taking control. So we cannot use those method to resolve Promise or queue task to notify the nodejs.
const JvmContextClass = Java.type("com.example.Executor")
const jvmContext = new JvmContextClass()
const myPromise = new Promise(jvmContext);
await myPromise
package com.example

class Executor {

    void then(Value resolve, Value reject) {
        Value setImmediate = resolve.getContext().eval("js", "setImmediate");
        Executors.newSingleThreadExecutor().execute(() => {
            try {
                Object someResult = computeSomething();
// Error, NodeJS is holding the context
                setImmediate.execute(() => {
                       resolve.executeVoid(someResult)
                }).asInt();
                resolve.executeVoid(someResult)
            } catch (Throwable t) {
// Error, NodeJS runtime is holding the context
                setImmediate.execute(() => {
                       reject.executeVoid(t);
                }).asInt();
                
            }
        })
// return control of current context back to NodeJS
    }

}

NodeJS task queue api and Promise-like object needs to support multi-threading out-of the box.

Creating nodejs worker thread to resolve one promise seems like pretty expensive.

Work Around

I make a workaround using Worker and Blocking Queue.
But it would be nice to use, something that nodeJS event loop can poll it, rather than blocking.
And still can not schedule task to NodeJS

data object JavaMain {
    @JvmStatic
    fun main(vararg args: String) {
//
        val mainContext = Context.getCurrent()
        val blockingQueue = LinkedBlockingQueue<Runnable>()
        val uuid = "X" + UUID.randomUUID().toString().replace("-","")
        val binding = mainContext.getBindings("js")
        val jsScript = """
            const { Worker } = require('worker_threads');
            const worker = new Worker(`
                const { workerData, parentPort } = require('worker_threads');
                const assert = require('assert');
                while (true) {
                    parentPort.postMessage(workerData.take());
                }
            `, { eval: true, workerData: $uuid })
             worker.on('message', block => {
                block()
            });
            worker;
        """.trimIndent()
        binding.putMember(uuid, blockingQueue)
        val proxyWorker = mainContext.eval("js", jsScript)
        mainContext.getBindings("js")
            .removeMember(uuid)
        blockingQueue.add{
            mainContext.eval("js", "console.log(1234)")
            println(proxyWorker)
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions