reactiverse / es4x

🚀 fast JavaScript 4 Eclipse Vert.x
https://reactiverse.io/es4x/
Apache License 2.0
877 stars 75 forks source link

Promise of vertx cannot call then twice or more. #587

Open fantasy0v0 opened 2 years ago

fantasy0v0 commented 2 years ago
$ es4x versions
VM:        OpenJDK 64-Bit Server VM - 17
VM Vendor: null
Vert.x:    4.2.2
ES4X:      0.16.2
graaljs:   21.3.0

code: index.js

import { Promise as VPromise } from "@vertx/core";
let promise = VPromise.promise();
promise.future().then(v => {
  console.log("v:" + v);
  return "v2";
}, (e) => {
  console.log("err");
}).then(v => {
  console.log("v2:" + v);
  return "v3";
}, (e) => {
  console.log("err");
});

error message:

Failed in deploying verticle caused by TypeError: Cannot read property 'then' of null
        at <js> :anonymous(/C:/Users/fan/Desktop/test/demo/index.js:21-31:412-606)
        at <js> _load(node_modules\.lib\es4x-0.16.2.jar!\io\reactiverse\es4x\jvm-npm.js:73:2195-2276)
        at <js> runMain(node_modules\.lib\es4x-0.16.2.jar!\io\reactiverse\es4x\jvm-npm.js:85:2567-2611)
        at org.graalvm.sdk/org.graalvm.polyglot.Value.invokeMember(Value.java:934)
        at io.reactiverse.es4x.impl.JSVerticleFactory$1.lambda$start$0(JSVerticleFactory.java:85)
        at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
        at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
        at io.vertx.core.impl.TaskQueue.run(TaskQueue.java:76)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
fantasy0v0 commented 2 years ago

then of io.reactiverse.es4x.Thenable Maybe should return new Future.

image

fantasy0v0 commented 2 years ago

then of io.reactiverse.es4x.Thenable Maybe should return new Future.

image

I tried to modify it, but the problem was not simple.😣https://promisesaplus.com/#the-then-method

fantasy0v0 commented 2 years ago

I try to implement this with native promises. I testing.

@Override
  public Value then(Value onResolve, Value onReject) {
    try {
      return JavaScript.createPromise(context, (_resolve, _reject) -> {
        Consumer<Object> onResolveNext = value -> {
          if (value instanceof Value _value) {
            if (JavaScript.isPromise(_value)) {
              _value.invokeMember("then", _resolve, _reject);
            } else {
              _resolve.execute(value);
            }
          } else {
            _resolve.execute(value);
          }
        };
        Consumer<Object> onRejectNext = err -> {
          if (err instanceof Value _value) {
            if (JavaScript.isPromise(_value)) {
              _value.invokeMember("then", _resolve, _reject);
            } else {
              _reject.execute(err);
            }
          } else {
            _reject.execute(err);
          }
        };
        future.onComplete(ar -> {
          if (ar.succeeded()) {
            T result = ar.result();
            if (onResolve != null && onResolve.canExecute()) {
              Value value = onResolve.execute(result);
              onResolveNext.accept(value);
            } else {
              _resolve.execute(result);
            }
          } else {
            Throwable cause = ar.cause();
            if (onReject != null && onReject.canExecute()) {
              Value value = onReject.execute(cause);
              onRejectNext.accept(value);
            } else {
              _reject.execute(cause);
            }
          }
        });
      });
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

JavaScript.createPromise implement:

public static Value createPromise(Context context, PromiseExecutor executor) throws IOException {
    Value bindings = context.getBindings("js");
    bindings.putMember("executor", executor);
    try {
      Source source = Source
        .newBuilder("js", "new Promise(executor)", null)
        .build();
      return context.eval(source);
    } finally {
      bindings.removeMember("executor");
    }
  }
pmlopes commented 2 years ago

@fantasy0v0 yes, that is the vert.x behavior, JavaScript has a similar behavior:

let myPromise = new Promise(function(myResolve, myReject) {
  myResolve('OK'); // when successful
  myResolve('OK'); // when successful
});

// "Consuming Code" (Must wait for a fulfilled Promise)
myPromise.then(
  function(value) { console.log(value); },
  function(error) { console.log(error); }
);

With a small difference that calling multiple times will silently ignore the multiple calls. Let's update es4x to be like that, ignore the multiple calls.