All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.landawn.abacus.util.ContinuableFuture Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.2.4
Show newest version
/*
 * Copyright (C) 2016 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.landawn.abacus.util;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.util.Tuple.Tuple4;

/**
 *
 * @author Haiyang Li
 * @param 
 * @see https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ContinuableFuture.html
 * @since 0.8
 * @see Futures
 */
public class ContinuableFuture implements Future {

    static final Logger logger = LoggerFactory.getLogger(ContinuableFuture.class);

    final Future future;

    final List> upFutures;

    final Executor asyncExecutor;

    ContinuableFuture(final Future future) {
        this(future, null, null);
    }

    ContinuableFuture(final Future future, final List> upFutures, final Executor asyncExecutor) {
        this.future = future;
        this.upFutures = upFutures;
        this.asyncExecutor = asyncExecutor == null ? N.asyncExecutor.getExecutor() : asyncExecutor;
    }

    /**
     *
     * @param 
     * @param action
     * @return
     * @see N#asyncExecute(Throwables.Runnable)
     */
    public static  ContinuableFuture run(final Throwables.Runnable action) {
        return run(action, N.asyncExecutor.getExecutor());
    }

    /**
     *
     * @param 
     * @param action
     * @param executor
     * @return
     */
    public static  ContinuableFuture run(final Throwables.Runnable action, final Executor executor) {
        final FutureTask futureTask = new FutureTask<>(() -> {
            action.run();
            return null;
        });

        executor.execute(futureTask);

        return new ContinuableFuture<>(futureTask, null, executor);
    }

    /**
     * 
     *
     * @param  
     * @param action 
     * @return 
     * @see N#asyncExecute(Callable)
     */
    public static  ContinuableFuture call(final Callable action) {
        return call(action, N.asyncExecutor.getExecutor());
    }

    /**
     * 
     *
     * @param  
     * @param action 
     * @param executor 
     * @return 
     */
    public static  ContinuableFuture call(final Callable action, final Executor executor) {
        final FutureTask futureTask = new FutureTask<>(action::call);

        executor.execute(futureTask);

        return new ContinuableFuture<>(futureTask, null, executor);
    }

    /**
     *
     * @param 
     * @param result
     * @return a ContinuableFuture which is already done by passing the result to it directly.
     */
    public static  ContinuableFuture completed(final T result) {
        return new ContinuableFuture<>(new Future() {
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                return false;
            }

            @Override
            public boolean isCancelled() {
                return false;
            }

            @Override
            public boolean isDone() {
                return true;
            }

            @Override
            public T get() {
                return result;
            }

            @Override
            public T get(final long timeout, final TimeUnit unit) {
                return result;
            }
        }, null, N.asyncExecutor.getExecutor());
    }

    /**
     *
     * @param 
     * @param future
     * @return
     */
    public static  ContinuableFuture wrap(Future future) {
        return new ContinuableFuture<>(future);
    }

    /**
     *
     * @param mayInterruptIfRunning
     * @return
     */
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return future.cancel(mayInterruptIfRunning);
    }

    /**
     * Checks if is cancelled.
     *
     * @return true, if is cancelled
     */
    @Override
    public boolean isCancelled() {
        return future.isCancelled();
    }

    /**
     * Cancel this future and all the previous stage future recursively.
     *
     * @param mayInterruptIfRunning
     * @return
     */
    public boolean cancelAll(boolean mayInterruptIfRunning) {
        boolean res = true;

        if (N.notNullOrEmpty(upFutures)) {
            for (ContinuableFuture preFuture : upFutures) {
                res = res & preFuture.cancelAll(mayInterruptIfRunning); //NOSONAR
            }
        }

        return cancel(mayInterruptIfRunning) && res;
    }

    /**
     * Returns true if this future and all previous stage futures have been recursively cancelled, otherwise false is returned.
     *
     * @return true, if is all cancelled
     */
    public boolean isAllCancelled() {
        if (N.notNullOrEmpty(upFutures)) {
            for (ContinuableFuture preFuture : upFutures) {
                if (preFuture.isAllCancelled()) {
                    return false;
                }
            }
        }

        return isCancelled();
    }

    /**
     * Checks if is done.
     *
     * @return true, if is done
     */
    @Override
    public boolean isDone() {
        return future.isDone();
    }

    /**
     *
     * @return
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     */
    @Override
    public T get() throws InterruptedException, ExecutionException {
        return future.get();
    }

    /**
     *
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     * @throws TimeoutException the timeout exception
     */
    @Override
    public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return future.get(timeout, unit);
    }

    /**
     * Gets the t.
     *
     * @return
     */
    public Result gett() {
        try {
            return Result.of(get(), null);
        } catch (Exception e) {
            return Result.of(null, e);
        }
    }

    /**
     * Gets the t.
     *
     * @param timeout
     * @param unit
     * @return
     */
    public Result gett(final long timeout, final TimeUnit unit) {
        try {
            return Result.of(get(timeout, unit), null);
        } catch (Exception e) {
            return Result.of(null, e);
        }
    }

    /**
     * Gets the now.
     *
     * @param defaultValue 
     * @return 
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     */
    public T getNow(T defaultValue) throws InterruptedException, ExecutionException {
        if (isDone()) {
            return get();
        }

        return defaultValue;
    }

    /**
     * Gets the then apply.
     *
     * @param 
     * @param 
     * @param action
     * @return
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     * @throws E the e
     */
    public  U getThenApply(final Throwables.Function action) throws InterruptedException, ExecutionException, E {
        return action.apply(get());
    }

    /**
     * Gets the then apply.
     *
     * @param 
     * @param 
     * @param timeout
     * @param unit
     * @param action
     * @return
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     * @throws TimeoutException the timeout exception
     * @throws E the e
     */
    public  U getThenApply(final long timeout, final TimeUnit unit, final Throwables.Function action)
            throws InterruptedException, ExecutionException, TimeoutException, E {
        return action.apply(get(timeout, unit));
    }

    /**
     * Gets the then apply.
     *
     * @param 
     * @param 
     * @param action
     * @return
     * @throws E the e
     */
    public  U getThenApply(final Throwables.BiFunction action) throws E {
        final Result result = gett();
        return action.apply(result.orElseIfFailure(null), result.getException());
    }

    /**
     * Gets the then apply.
     *
     * @param 
     * @param 
     * @param timeout
     * @param unit
     * @param action
     * @return
     * @throws E the e
     */
    public  U getThenApply(final long timeout, final TimeUnit unit,
            final Throwables.BiFunction action) throws E {
        final Result result = gett(timeout, unit);
        return action.apply(result.orElseIfFailure(null), result.getException());
    }

    /**
     * Gets the then accept.
     *
     * @param  
     * @param action 
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     * @throws E the e
     */
    public  void getThenAccept(final Throwables.Consumer action) throws InterruptedException, ExecutionException, E {
        action.accept(get());
    }

    /**
     * Gets the then accept.
     *
     * @param  
     * @param timeout 
     * @param unit 
     * @param action 
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException the execution exception
     * @throws TimeoutException the timeout exception
     * @throws E the e
     */
    public  void getThenAccept(final long timeout, final TimeUnit unit, final Throwables.Consumer action)
            throws InterruptedException, ExecutionException, TimeoutException, E {
        action.accept(get(timeout, unit));
    }

    /**
     * Gets the then accept.
     *
     * @param  
     * @param action 
     * @throws E the e
     */
    public  void getThenAccept(final Throwables.BiConsumer action) throws E {
        final Result result = gett();
        action.accept(result.orElseIfFailure(null), result.getException());
    }

    /**
     * Gets the then accept.
     *
     * @param  
     * @param timeout 
     * @param unit 
     * @param action 
     * @throws E the e
     */
    public  void getThenAccept(final long timeout, final TimeUnit unit,
            final Throwables.BiConsumer action) throws E {
        final Result result = gett(timeout, unit);
        action.accept(result.orElseIfFailure(null), result.getException());
    }

    //    public void complete() throws InterruptedException, ExecutionException {
    //        get();
    //    }
    //
    //    public void complete(Consumer action) {
    //        try {
    //            action.accept(get());
    //        } catch (InterruptedException | ExecutionException e) {
    //            throw ExceptionUtil.toRuntimeException(e);
    //        }
    //    }
    //
    //    public void complete(BiConsumer action) {
    //        final Result result = gett();
    //        action.accept(result.orElse(null), result.getException());
    //    }

    /**
     *
     * @param 
     * @param 
     * @param func
     * @return
     */
    public  ContinuableFuture map(final Throwables.Function func) {
        return new ContinuableFuture<>(new Future() {
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                return ContinuableFuture.this.cancel(mayInterruptIfRunning);
            }

            @Override
            public boolean isCancelled() {
                return ContinuableFuture.this.isCancelled();
            }

            @Override
            public boolean isDone() {
                return ContinuableFuture.this.isDone();
            }

            @Override
            public U get() throws InterruptedException, ExecutionException {
                final T ret = ContinuableFuture.this.get();

                try {
                    return func.apply(ret);
                } catch (Exception e) {
                    throw ExceptionUtil.toRuntimeException(e);
                }
            }

            @Override
            public U get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                final T ret = ContinuableFuture.this.get(timeout, unit);

                try {
                    return func.apply(ret);
                } catch (Exception e) {
                    throw ExceptionUtil.toRuntimeException(e);
                }
            }
        }, null, asyncExecutor) {
            @Override
            public boolean cancelAll(boolean mayInterruptIfRunning) {
                return ContinuableFuture.this.cancelAll(mayInterruptIfRunning);
            }

            @Override
            public boolean isAllCancelled() {
                return ContinuableFuture.this.isAllCancelled();
            }
        };
    }

    //    public  ContinuableFuture thenApply(final BiFunction action) {
    //        return new ContinuableFuture(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone();
    //            }
    //
    //            @Override
    //            public U get() throws InterruptedException, ExecutionException {
    //                final Result result = gett();
    //                return action.apply(result.orElse(null), result.getException());
    //            }
    //
    //            @Override
    //            public U get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                final Result result = gett(timeout, unit);
    //                return action.apply(result.orElse(null), result.getException());
    //            }
    //        }, null, asyncExecutor) {
    //            @Override
    //            public boolean cancelAll(boolean mayInterruptIfRunning) {
    //                return super.cancelAll(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isAllCancelled() {
    //                return super.isAllCancelled();
    //            }
    //        };
    //    }

    //    public  ContinuableFuture thenAccept(final Consumer action) {
    //        return thenApply(new Function() {
    //            @Override
    //            public Void apply(T t) {
    //                action.accept(t);
    //                return null;
    //            }
    //        });
    //    }
    //
    //    public  ContinuableFuture thenAccept(final BiConsumer action) {
    //        return thenApply(new BiFunction() {
    //            @Override
    //            public Void apply(T t, ? super Exception e) {
    //                action.accept(t, e);
    //                return null;
    //            }
    //        });
    //    }
    //
    //    public  ContinuableFuture thenCombine(final ContinuableFuture other, final BiFunction action) {
    //        return new ContinuableFuture(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning) && other.future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled() || other.future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone() && other.future.isDone();
    //            }
    //
    //            @Override
    //            public R get() throws InterruptedException, ExecutionException {
    //                return action.apply(future.get(), other.future.get());
    //            }
    //
    //            @Override
    //            public R get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                final long timeoutInMillis = unit.toMillis(timeout);
    //                final long now = N.currentMillis();
    //                final long endTime = timeoutInMillis > Long.MAX_VALUE - now ? Long.MAX_VALUE : now + timeoutInMillis;
    //
    //                final T result = future.get(timeout, unit);
    //                final U otherResult = other.future.get(N.max(0, endTime - N.currentMillis()), TimeUnit.MILLISECONDS);
    //
    //                return action.apply(result, otherResult);
    //            }
    //        }, null, asyncExecutor) {
    //            @Override
    //            public boolean cancelAll(boolean mayInterruptIfRunning) {
    //                return super.cancelAll(mayInterruptIfRunning) && other.cancelAll(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isAllCancelled() {
    //                return super.isAllCancelled() && other.isAllCancelled();
    //            }
    //        };
    //    }
    //
    //    public  ContinuableFuture thenCombine(final ContinuableFuture other, final Function, ? extends R> action) {
    //        return new ContinuableFuture(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning) && other.future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled() || other.future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone() && other.future.isDone();
    //            }
    //
    //            @Override
    //            public R get() throws InterruptedException, ExecutionException {
    //                final Result result = gett();
    //                final Pair result2 = other.gett();
    //
    //                return action.apply(Tuple.of(result.orElse(null), result.getException(), (U) result2.orElse(null), result2.getException()));
    //            }
    //
    //            @Override
    //            public R get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                final long timeoutInMillis = unit.toMillis(timeout);
    //                final long now = N.currentMillis();
    //                final long endTime = timeoutInMillis > Long.MAX_VALUE - now ? Long.MAX_VALUE : now + timeoutInMillis;
    //
    //                final Result result = ContinuableFuture.this.gett(timeout, unit);
    //                final Pair result2 = other.gett(N.max(0, endTime - N.currentMillis()), TimeUnit.MILLISECONDS);
    //
    //                return action.apply(Tuple.of(result.orElse(null), result.getException(), (U) result2.orElse(null), result2.getException()));
    //            }
    //        }, null, asyncExecutor) {
    //            @Override
    //            public boolean cancelAll(boolean mayInterruptIfRunning) {
    //                return super.cancelAll(mayInterruptIfRunning) && other.cancelAll(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isAllCancelled() {
    //                return super.isAllCancelled() && other.isAllCancelled();
    //            }
    //        };
    //    }
    //
    //    public  ContinuableFuture thenAcceptBoth(final ContinuableFuture other, final BiConsumer action) {
    //        return thenCombine(other, new BiFunction() {
    //            @Override
    //            public Void apply(T t, U u) {
    //                action.accept(t, u);
    //                return null;
    //            }
    //        });
    //    }
    //
    //    public  ContinuableFuture thenAcceptBoth(final ContinuableFuture other, final Consumer> action) {
    //        return thenCombine(other, new Function, Void>() {
    //            @Override
    //            public Void apply(Tuple4 t) {
    //                action.accept(t);
    //                return null;
    //            }
    //        });
    //    }

    /**
     *
     * @param 
     * @param action
     * @return
     */
    public  ContinuableFuture thenRun(final Throwables.Runnable action) {
        return execute(() -> {
            get();
            action.run();
            return null;
        });
    }

    /**
     *
     * @param 
     * @param action
     * @return
     */
    public  ContinuableFuture thenRun(final Throwables.Consumer action) {
        return execute(() -> {
            action.accept(get());
            return null;
        });
    }

    /**
     *
     * @param 
     * @param action
     * @return
     */
    public  ContinuableFuture thenRun(final Throwables.BiConsumer action) {
        return execute(() -> {
            final Result result = gett();
            action.accept(result.orElseIfFailure(null), result.getException());
            return null;
        });
    }

    /**
     * 
     *
     * @param  
     * @param action 
     * @return 
     */
    public  ContinuableFuture thenCall(final Callable action) {
        return execute(() -> {
            get();
            return action.call();
        });
    }

    /**
     *
     * @param 
     * @param 
     * @param action
     * @return
     */
    public  ContinuableFuture thenCall(final Throwables.Function action) {
        return execute(() -> action.apply(get()));
    }

    /**
     *
     * @param 
     * @param 
     * @param action
     * @return
     */
    public  ContinuableFuture thenCall(final Throwables.BiFunction action) {
        return execute(() -> {
            final Result result = gett();
            return action.apply(result.orElseIfFailure(null), result.getException());
        });
    }

    /**
     * Run after both.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterBoth(final ContinuableFuture other, final Throwables.Runnable action) {
        return execute(() -> {
            get();
            other.get();
            action.run();
            return null;
        }, other);
    }

    /**
     * Run after both.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterBoth(final ContinuableFuture other,
            final Throwables.BiConsumer action) {
        return execute(() -> {
            action.accept(get(), other.get());
            return null;
        }, other);
    }

    /**
     * Run after both.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterBoth(final ContinuableFuture other,
            final Throwables.Consumer, E> action) {
        return execute(() -> {
            final Result result = gett();
            final Result result2 = other.gett();

            action.accept(Tuple.of(result.orElseIfFailure(null), result.getException(), result2.orElseIfFailure(null), result2.getException()));
            return null;
        }, other);
    }

    /**
     * Run after both.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterBoth(final ContinuableFuture other,
            final Throwables.QuadConsumer action) {
        return execute(() -> {
            final Result result = gett();
            final Result result2 = other.gett();

            action.accept(result.orElseIfFailure(null), result.getException(), result2.orElseIfFailure(null), result2.getException());
            return null;
        }, other);
    }

    /**
     * Call after both.
     *
     * @param  
     * @param other 
     * @param action 
     * @return 
     */
    public  ContinuableFuture callAfterBoth(final ContinuableFuture other, final Callable action) {
        return execute(() -> {
            get();
            other.get();
            return action.call();
        }, other);
    }

    /**
     * Call after both.
     *
     * @param 
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterBoth(final ContinuableFuture other,
            final Throwables.BiFunction action) {
        return execute(() -> action.apply(get(), other.get()), other);
    }

    /**
     * Call after both.
     *
     * @param 
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterBoth(final ContinuableFuture other,
            final Throwables.Function, ? extends R, E> action) {
        return execute(() -> {
            final Result result = gett();
            final Result result2 = other.gett();

            return action.apply(Tuple.of(result.orElseIfFailure(null), result.getException(), result2.orElseIfFailure(null), result2.getException()));
        }, other);
    }

    /**
     * Call after both.
     *
     * @param 
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterBoth(final ContinuableFuture other,
            final Throwables.QuadFunction action) {
        return execute(() -> {
            final Result result = gett();
            final Result result2 = other.gett();

            return action.apply(result.orElseIfFailure(null), result.getException(), result2.orElseIfFailure(null), result2.getException());
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterEither(final ContinuableFuture other, final Throwables.Runnable action) {
        return execute(() -> {
            Futures.anyOf(Array.asList(ContinuableFuture.this, other)).get();

            action.run();
            return null;
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterEither(final ContinuableFuture other,
            final Throwables.Consumer action) {
        return execute(() -> {
            final T result = Futures.anyOf(Array.asList(ContinuableFuture.this, other)).get();

            action.accept(result);
            return null;
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterEither(final ContinuableFuture other,
            final Throwables.BiConsumer action) {
        return execute(() -> {
            final Result result = Futures.anyOf(Array.asList(ContinuableFuture.this, other)).gett();

            action.accept(result.orElseIfFailure(null), result.getException());
            return null;
        }, other);
    }

    /**
     * Call after either.
     *
     * @param  
     * @param other 
     * @param action 
     * @return 
     */
    public  ContinuableFuture callAfterEither(final ContinuableFuture other, final Callable action) {
        return execute(() -> {
            Futures.anyOf(Array.asList(ContinuableFuture.this, other)).get();

            return action.call();
        }, other);
    }

    /**
     * Call after either.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterEither(final ContinuableFuture other,
            final Throwables.Function action) {
        return execute(() -> {
            final T result = Futures.anyOf(Array.asList(ContinuableFuture.this, other)).get();

            return action.apply(result);
        }, other);
    }

    /**
     * Call after either.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterEither(final ContinuableFuture other,
            final Throwables.BiFunction action) {
        return execute(() -> {
            final Result result = Futures.anyOf(Array.asList(ContinuableFuture.this, other)).gett();

            return action.apply(result.orElseIfFailure(null), result.getException());
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterFirstSucceed(final ContinuableFuture other, final Throwables.Runnable action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isFailure()) {
                    throw firstResult.getException();
                }
            }

            action.run();
            return null;
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterFirstSucceed(final ContinuableFuture other,
            final Throwables.Consumer action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();
            T ret = null;

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isFailure()) {
                    throw firstResult.getException();
                } else {
                    ret = secondResult.orElseIfFailure(null);
                }
            } else {
                ret = firstResult.orElseIfFailure(null);
            }

            action.accept(ret);

            return null;
        }, other);
    }

    /**
     * Run after either.
     *
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture runAfterFirstSucceed(final ContinuableFuture other,
            final Throwables.BiConsumer action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();
            Result ret = null;

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isSuccess()) {
                    ret = secondResult;
                }
            }

            if (ret == null) {
                ret = firstResult;
            }

            action.accept(ret.orElseIfFailure(null), ret.getException());
            return null;
        }, other);
    }

    /**
     * Call after either.
     *
     * @param  
     * @param other 
     * @param action 
     * @return 
     */
    public  ContinuableFuture callAfterFirstSucceed(final ContinuableFuture other, final Callable action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isFailure()) {
                    throw firstResult.getException();
                }
            }

            return action.call();
        }, other);
    }

    /**
     * Call after either.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterFirstSucceed(final ContinuableFuture other,
            final Throwables.Function action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();
            T ret = null;

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isFailure()) {
                    throw firstResult.getException();
                } else {
                    ret = secondResult.orElseIfFailure(null);
                }
            } else {
                ret = firstResult.orElseIfFailure(null);
            }

            return action.apply(ret);
        }, other);
    }

    /**
     * Call after either.
     *
     * @param 
     * @param 
     * @param other
     * @param action
     * @return
     */
    public  ContinuableFuture callAfterFirstSucceed(final ContinuableFuture other,
            final Throwables.BiFunction action) {
        return execute(() -> {
            final ObjIterator> iter = Futures.iteratte(ContinuableFuture.this, other);
            final Result firstResult = iter.next();
            Result ret = null;

            if (firstResult.isFailure()) {
                final Result secondResult = iter.next();

                if (secondResult.isSuccess()) {
                    ret = secondResult;
                }
            }

            if (ret == null) {
                ret = firstResult;
            }

            return action.apply(ret.orElseIfFailure(null), ret.getException());
        }, other);
    }

    //    /**
    //     * Returns a new ContinuableFuture that, when either this or the
    //     * other given ContinuableFuture complete normally. If both of the given
    //     * ContinuableFutures complete exceptionally, then the returned
    //     * ContinuableFuture also does so.
    //     *
    //     * @param other
    //     * @param action
    //     * @return
    //     */
    //    public  ContinuableFuture applyToEither(final ContinuableFuture other, final Function action) {
    //        return Futures.anyOf(Array.asList(this, other)).thenApply(action);
    //    }
    //
    //    /**
    //     * Returns a new ContinuableFuture that, when either this or the
    //     * other given ContinuableFuture complete normally. If both of the given
    //     * ContinuableFutures complete exceptionally, then the returned
    //     * ContinuableFuture also does so.
    //     *
    //     * @param other
    //     * @param action
    //     * @return
    //     */
    //    public ContinuableFuture acceptEither(final ContinuableFuture other, final Consumer action) {
    //        return Futures.anyOf(Array.asList(this, other)).thenAccept(action);
    //    }

    //    /**
    //     * Returns a new ContinuableFuture that, when this ContinuableFuture completes
    //     * exceptionally, is executed with this ContinuableFuture's exception as the
    //     * argument to the supplied function. Otherwise, if this ContinuableFuture
    //     * completes normally, then the returned ContinuableFuture also completes
    //     * normally with the same value.
    //     *
    //     * @param action
    //     * @return
    //     */
    //    public ContinuableFuture exceptionally(final Function action) {
    //        return new ContinuableFuture(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone();
    //            }
    //
    //            @Override
    //            public T get() throws InterruptedException, ExecutionException {
    //                try {
    //                    return future.get();
    //                } catch (Exception e) {
    //                    return action.apply(e);
    //                }
    //            }
    //
    //            @Override
    //            public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                try {
    //                    return future.get(timeout, unit);
    //                } catch (Exception e) {
    //                    return action.apply(e);
    //                }
    //            }
    //        }, null, asyncExecutor) {
    //            @Override
    //            public boolean cancelAll(boolean mayInterruptIfRunning) {
    //                return super.cancelAll(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isAllCancelled() {
    //                return super.isAllCancelled();
    //            }
    //        };
    //    }

    //    public ContinuableFuture whenComplete(final BiConsumer action) {
    //        return new ContinuableFuture<>(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone();
    //            }
    //
    //            @Override
    //            public T get() throws InterruptedException, ExecutionException {
    //                final Result result = gett();
    //
    //                if (result.right != null) {
    //                    try {
    //                        action.accept(result.orElse(null), result.getException());
    //                    } catch (Exception e) {
    //                        // ignore.
    //                    }
    //
    //                    if (result.right instanceof InterruptedException) {
    //                        throw ((InterruptedException) result.right);
    //                    } else if (result.right instanceof ExecutionException) {
    //                        throw ((ExecutionException) result.right);
    //                    } else {
    //                        throw ExceptionUtil.toRuntimeException(result.right);
    //                    }
    //                } else {
    //                    action.accept(result.orElse(null), result.getException());
    //                    return result.left;
    //                }
    //            }
    //
    //            @Override
    //            public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                final Result result = gett(timeout, unit);
    //
    //                if (result.right != null) {
    //                    try {
    //                        action.accept(result.orElse(null), result.getException());
    //                    } catch (Exception e) {
    //                        // ignore.
    //                    }
    //
    //                    if (result.right instanceof InterruptedException) {
    //                        throw ((InterruptedException) result.right);
    //                    } else if (result.right instanceof ExecutionException) {
    //                        throw ((ExecutionException) result.right);
    //                    } else {
    //                        throw ExceptionUtil.toRuntimeException(result.right);
    //                    }
    //                } else {
    //                    action.accept(result.orElse(null), result.getException());
    //                    return result.left;
    //                }
    //            }
    //        }, asyncExecutor);
    //    }
    //
    //    public  ContinuableFuture handle(final BiFunction action) {
    //        return new ContinuableFuture<>(new Future() {
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return future.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return future.isCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return future.isDone();
    //            }
    //
    //            @Override
    //            public U get() throws InterruptedException, ExecutionException {
    //                final Result result = gett();
    //                return action.apply(result.orElse(null), result.getException());
    //            }
    //
    //            @Override
    //            public U get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                final Result result = gett(timeout, unit);
    //                return action.apply(result.orElse(null), result.getException());
    //            }
    //        }, asyncExecutor);
    //    }

    /**
     *
     * @param 
     * @param command
     * @return
     */
    private  ContinuableFuture execute(final Callable command) {
        return execute(command, null);
    }

    /**
     *
     * @param 
     * @param command
     * @param other
     * @return
     */
    private  ContinuableFuture execute(final Callable command, final ContinuableFuture other) {
        return execute(new FutureTask<>(command), other);
    }

    /**
     *
     * @param 
     * @param futureTask
     * @param other
     * @return
     */
    private  ContinuableFuture execute(final FutureTask futureTask, final ContinuableFuture other) {
        asyncExecutor.execute(futureTask);

        @SuppressWarnings("rawtypes")
        final List> upFutureList = other == null ? (List) Arrays.asList(this) : Arrays.asList(this, other);
        return new ContinuableFuture<>(futureTask, upFutureList, asyncExecutor);
    }

    /**
     *
     * @param delay
     * @param unit
     * @return
     */
    public ContinuableFuture thenDelay(long delay, TimeUnit unit) {
        if (delay <= 0) {
            return this;
        }

        return with(asyncExecutor, delay, unit);
    }

    /**
     *
     * @param executor
     * @return
     */
    public ContinuableFuture thenUse(Executor executor) {
        return with(executor, 0, TimeUnit.MILLISECONDS);
    }

    /**
     *
     * @param executor
     * @param delay
     * @param unit
     * @return
     * @deprecated
     */
    @Deprecated
    ContinuableFuture with(final Executor executor, final long delay, final TimeUnit unit) {
        N.checkArgNotNull(executor);

        return new ContinuableFuture<>(new Future() {
            private final long delayInMillis = unit.toMillis(delay);
            private final long startTime = System.currentTimeMillis();
            private volatile boolean isDelayed = false;

            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                return future.cancel(mayInterruptIfRunning);
            }

            @Override
            public boolean isCancelled() {
                return future.isCancelled();
            }

            @Override
            public boolean isDone() {
                boolean isDone = future.isDone();

                if (isDone) {
                    delay();
                }

                return isDone;
            }

            @Override
            public T get() throws InterruptedException, ExecutionException {
                delay();

                return future.get();
            }

            @Override
            public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                delay();

                return future.get(timeout, unit);
            }

            private void delay() {
                if (!isDelayed) {
                    isDelayed = true;

                    N.sleepUninterruptibly(delayInMillis - (System.currentTimeMillis() - startTime));
                }
            }
        }, null, executor) {
            @Override
            public boolean cancelAll(boolean mayInterruptIfRunning) { //NOSONAR
                return super.cancelAll(mayInterruptIfRunning);
            }

            @Override
            public boolean isAllCancelled() { //NOSONAR
                return super.isAllCancelled();
            }
        };
    }

    // Doesn't work.
    //    public CompletableFuture toCompletableFuture() {
    //        return new CompletableFuture() {
    //
    //            @Override
    //            public boolean cancel(boolean mayInterruptIfRunning) {
    //                return ContinuableFuture.this.cancel(mayInterruptIfRunning);
    //            }
    //
    //            @Override
    //            public boolean isCancelled() {
    //                return ContinuableFuture.this.isAllCancelled();
    //            }
    //
    //            @Override
    //            public boolean isDone() {
    //                return ContinuableFuture.this.isDone();
    //            }
    //
    //            @Override
    //            public T get() throws InterruptedException, ExecutionException {
    //                return ContinuableFuture.this.get();
    //            }
    //
    //            @Override
    //            public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    //                return ContinuableFuture.this.get(timeout, unit);
    //            }
    //        };
    //    }
}