oracle / graaljs

GraalJS – A high-performance, ECMAScript compliant, and embeddable JavaScript runtime for Java
https://www.graalvm.org/javascript/
Universal Permissive License v1.0
1.81k stars 190 forks source link

ReferenceError: setTimeout is not defined #317

Closed pragmasoft-ua closed 4 years ago

pragmasoft-ua commented 4 years ago

When I'm trying to use polyglot context to evaluate simple js script containing global setTimeout function, I get the following error:

ReferenceError: setTimeout is not defined

The same error I obtain when use graalvm js binary.

Though, script works properly, when I execute it using graalvm node binary.

Is there any way to evaluate script from polyglot context in node compatibility mode?

I tried context.eval("node", script) and tried using .option("js.v8-compat", "true") in Context.Builder without any success.

wirthi commented 4 years ago

Hi @pragmasoft-ua

thanks for your question.

setTimeout is not defined in JavaScript (ECMAScript). It is an extension typically provided by the host environment (the Browser, the Node.js framework). This is the reason why you don't have it in our js binary (providing only pure JavaScript, with few exceptions). The function is available in the graalvm/bin/node executable.

Starting a node process from Java with something like context.eval("node", script) is an interesting idea, that we are investigating on a long-term scale. It is a technical challenge, as both the JVM and Node.js are binary applications, that take control of resources, signals, threads, etc. and don't expect another such application appear on the same process.

We have solved this in the other direction, though. You can start a JavaScript/Node.js application via graalvm/bin/node --jvm and then use our JavaScript-Java interoperability to move into Java land from there. You need to take about multithreading, but in general, it is possible.

Best, Christian

StephenOTT commented 3 years ago

Was there any workaround to this issue for use with polyglot?

Vetka-sakury commented 5 months ago

The question is still actual...

iamstolis commented 5 months ago

setTimeout is a function that should be implemented by the embedder of the JavaScript engine. It is the same for Node.js. The function that you see there does not come from V8 (the JavaScript engine used by the original Node.js) but is provided by Node.js runtime (= the embedder of the JavaScript engine).

If you embed graal-js into your Java application then you can use something like

public static void main(String[] args) throws Exception {
    Context context = Context.newBuilder("js").allowAllAccess(true).build();
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    TimeoutFunction setTimeout = (fn, delay) -> executor.schedule(() -> fn.execute(), delay, TimeUnit.MILLISECONDS);

    // all JavaScript tasks are executed through the executor to ensure a single-threaded access
    executor.schedule(() -> {
            // define/initialize setTimeout function
            context.getBindings("js").putMember("setTimeout", setTimeout);

            // try to use setTimeout function
            context.eval("js", "console.log('See you in 5s.'); setTimeout(() => console.log('I am back!'), 5000)");
            executor.shutdown();
        }, 0, TimeUnit.MILLISECONDS).get();
}

@FunctionalInterface
public static interface TimeoutFunction {
    void setTimeout(Value fn, int delay);
}
pragmasoft-ua commented 5 months ago

setTimeout is a function that should be implemented by the embedder of the JavaScript engine

You probably realize that the problem is not with the setTimeout only. Without the possibility to use 3rd party libraries this feature has almost no value, nobody will implement in java entire node runtime.

I don't understand why you cannot expose the existing node runtime you have already implemented in java to use by java / polyglot developers.

There are 4 years passed since I opened this issue, and still I don't see any popular frameworks emerged using this polyglot feature. The main reason is the lack of possibility of using 3rd party libraries.

iamstolis commented 5 months ago

I don't understand why you cannot expose the existing node runtime you have already implemented in java to use by java / polyglot developers.

If you want to use Java and Node.js features then you should use graal-nodejs. Node.js is not a library, it is a runtime. You cannot simply expose its builti-ns in Polyglot API in an arbitrary Java application. There is much more in Node.js (event loop, signals etc.)

pragmasoft-ua commented 5 months ago

You cannot simply expose its builti-ns in Polyglot API in an arbitrary Java application. There is much more in Node.js (event loop, signals etc.)

Really? Just add an entry point to start event loop and expose it as binding? Is it too complex?

caoccao commented 5 months ago

still I don't see any popular frameworks emerged using this polyglot feature. The main reason is the lack of possibility of using 3rd party libraries.

That's an interesting point. As far as I know, there are no popular frameworks built on top of Node.js in JVM. The reason to my understanding is there is no need to do so because a genuine Node.js in JVM (not GraalJS) natively supports all kinds of 3rd party libraries. E.g. There are tens of Minecraft mod developers use such a Node.js in JVM solution to bridge the JS scripts with the Minecraft SDK.