mszmurlo
mszmurlo

Reputation: 1339

Vert.x: How to wait for a future to complete

Is there a way to wait for a future to complete without blocking the event loop?

An example of a use case with querying Mongo:

Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
    if(res.succeeded()) {
      ...
      dbFut.complete(res.result());
    }
    else {
      ...
      dbFut.fail(res.cause());
    }
  }
  });

// Here I need the result of the DB query
if(dbFut.succeeded()) {
  doSomethingWith(dbFut.result());
}
else {
  error();
}

I know the doSomethingWith(dbFut.result()); can be moved to the handler, yet if it's long, the code will get unreadable (Callback hell ?) It that the right solution ? Is that the omny solution without additional libraries ?

I'm aware that rxJava simplifies the code, but as I don't know it, learning Vert.x and rxJava is just too much.

I also wanted to give a try to vertx-sync. I put the dependency in the pom.xml; everything got downloaded fine but when I started my app, I got the following error

maurice@mickey> java \
  -javaagent:~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar \
  -jar target/app-dev-0.1-fat.jar \
  -conf conf/config.json 
Error opening zip file or JAR manifest missing : ~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar
Error occurred during initialization of VM
agent library failed to init: instrument

I know what the error means in general, but I don't know in that context... I tried to google for it but didn't find any clear explanation about which manifest to put where. And as previously, unless mandatory, I prefer to learn one thing at a time.

So, back to the question : is there a way with "basic" Vert.x to wait for a future without perturbation on the event loop ?

Upvotes: 6

Views: 15925

Answers (2)

kevinarpe
kevinarpe

Reputation: 21309

I agree that you should not block in the Vertx processing pipeline, but I make one exception to that rule: Start-up. By design, I want to block while my HTTP server is initialising.

This code might help you:

/**
 * @return null when waiting on {@code Future<Void>}
 */
@Nullable
public static <T>
T awaitComplete(Future<T> f)
throws Throwable
{
    final Object lock = new Object();
    final AtomicReference<AsyncResult<T>> resultRef = new AtomicReference<>(null);
    synchronized (lock)
    {
        // We *must* be locked before registering a callback.
        // If result is ready, the callback is called immediately!
        f.onComplete(
            (AsyncResult<T> result) ->
            {
                resultRef.set(result);
                synchronized (lock) {
                    lock.notify();
                }
            });

        do {
            // If we get a spurious wake-up before resultRef is set,
            // we need to reacquire the lock, then wait again.
            // Ref: https://stackoverflow.com/a/249907/257299
            // @Blocking
            lock.wait();
        }
        while (null == resultRef.get());
    }

    final AsyncResult<T> result = resultRef.get();
    if (result.failed())
    {
        throw result.cause();
    }

    @Nullable
    final T x = result.result();
    return x;
}

Upvotes: 3

Mohamed Ibrahim Elsayed
Mohamed Ibrahim Elsayed

Reputation: 2964

You can set a handler for the future to be executed upon completion or failure:

Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
    if(res.succeeded()) {
      ...
      dbFut.complete(res.result());
    }
    else {
      ...
      dbFut.fail(res.cause());
    }
  }
  });

dbFut.setHandler(asyncResult -> {
    if(asyncResult.succeeded()) {
      // your logic here
    }
});

This is a pure Vert.x way that doesn't block the event loop

Upvotes: 5

Related Questions