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

hprose.util.concurrent.Promise Maven / Gradle / Ivy

/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * Promise.java                                           *
 *                                                        *
 * Promise class for Java.                                *
 *                                                        *
 * LastModified: Sep 19, 2016                             *
 * Author: Ma Bingyao                   *
 *                                                        *
\**********************************************************/
package hprose.util.concurrent;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public final class Promise implements Resolver, Rejector, Thenable {
    private static volatile ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
    static {
        Threads.registerShutdownHandler(new Runnable() {
            public void run() {
                ScheduledExecutorService t = timer;
                timer = Executors.newSingleThreadScheduledExecutor();
                t.shutdownNow();
            }
        });
    }

    private final LinkedList> subscribers = new LinkedList>();
    private volatile AtomicReference state = new AtomicReference(State.PENDING);
    private volatile V value;
    private volatile Throwable reason;

    public Promise() {}

    public Promise(final Call computation) {
        timer.execute(new Runnable() {
            public void run() {
                try {
                    Promise.this.resolve(computation.call());
                }
                catch (Throwable e) {
                    Promise.this.reject(e);
                }
            }
        });
    }

    public Promise(final AsyncCall computation) {
        timer.execute(new Runnable() {
            public void run() {
                try {
                    Promise.this.resolve(computation.call());
                }
                catch (Throwable e) {
                    Promise.this.reject(e);
                }
            }
        });
    }

    public Promise(Executor executor) {
        executor.exec((Resolver)this, (Rejector)this);
    }

    public final static  Promise value(T value) {
        Promise promise = new Promise();
        promise.resolve(value);
        return promise;
    }

    public final static  Promise value(Promise value) {
        Promise promise = new Promise();
        promise.resolve(value);
        return promise;
    }

    public final static  Promise value(Thenable value) {
        Promise promise = new Promise();
        promise.resolve(value);
        return promise;
    }

    public final static  Promise error(Throwable reason) {
        Promise promise = new Promise();
        promise.reject(reason);
        return promise;
    }

    public final static  Promise delayed(long duration, TimeUnit timeunit, final Call computation) {
        final Promise promise = new Promise();
        timer.schedule(new Runnable() {
            public void run() {
                try {
                    promise.resolve(computation.call());
                }
                catch (Throwable e) {
                    promise.reject(e);
                }
            }
        }, duration, timeunit);
        return promise;
    }

    public final static  Promise delayed(long duration, TimeUnit timeunit, final AsyncCall computation) {
        final Promise promise = new Promise();
        timer.schedule(new Runnable() {
            public void run() {
                try {
                    promise.resolve(computation.call());
                }
                catch (Throwable e) {
                    promise.reject(e);
                }
            }
        }, duration, timeunit);
        return promise;
    }

    public final static  Promise delayed(long duration, TimeUnit timeunit, final T value) {
        final Promise promise = new Promise();
        timer.schedule(new Runnable() {
            public void run() {
                promise.resolve(value);
            }
        }, duration, timeunit);
        return promise;
    }

    public final static  Promise delayed(long duration, TimeUnit timeunit, final Promise value) {
        final Promise promise = new Promise();
        timer.schedule(new Runnable() {
            public void run() {
                promise.resolve(value);
            }
        }, duration, timeunit);
        return promise;
    }

    public final static  Promise delayed(long duration, Call computation) {
        return delayed(duration, TimeUnit.MILLISECONDS, computation);
    }

    public final static  Promise delayed(long duration, AsyncCall computation) {
        return delayed(duration, TimeUnit.MILLISECONDS, computation);
    }

    public final static  Promise delayed(long duration, T value) {
        return delayed(duration, TimeUnit.MILLISECONDS, value);
    }

    public final static  Promise delayed(long duration, Promise value) {
        return delayed(duration, TimeUnit.MILLISECONDS, value);
    }

    public final static  Promise sync(Call computation) {
        try {
            return value(computation.call());
        }
        catch (Throwable e) {
            return error(e);
        }
    }

    public final static  Promise sync(AsyncCall computation) {
        try {
            return value(computation.call());
        }
        catch (Throwable e) {
            return error(e);
        }
    }

    public final static boolean isThenable(Object value) {
        return value instanceof Thenable;
    }

    public final static boolean isPromise(Object value) {
        return value instanceof Promise;
    }

    public final static Promise toPromise(Object value) {
        return isPromise(value) ? (Promise)value : value(value);
    }

    @SuppressWarnings("unchecked")
    private static  void allHandler(final Promise promise, final AtomicInteger count, final T[] result, Object element, final int i) {
        ((Promise)toPromise(element)).then(
            new Action() {
                public void call(T value) throws Throwable {
                    result[i] = value;
                    if (count.decrementAndGet() == 0) {
                        promise.resolve(result);
                    }
                }
            },
            new Action() {
                public void call(Throwable e) throws Throwable {
                    promise.reject(e);
                }
            }
        );
    }

    @SuppressWarnings("unchecked")
    public final static  Promise all(Object[] array, Class type) {
        if (array == null) return value((T[])null);
        int n = array.length;
        T[] result = (type == Object.class) ?
                (T[])(new Object[n]) :
                (T[])Array.newInstance(type, n);
        if (n == 0) return value(result);
        AtomicInteger count = new AtomicInteger(n);
        Promise promise = new Promise();
        for (int i = 0; i < n; ++i) {
            allHandler(promise, count, result, array[i], i);
        }
        return promise;
    }

    public final static Promise all(Object[] array) {
        return all(array, Object.class);
    }

    public final static  Promise all(Promise promise, final Class type) {
        return promise.then(new AsyncFunc() {
            public Promise call(Object[] array) throws Throwable {
                return all(array, type);
            }
        });
    }

    public final static Promise all(Promise promise) {
        return all(promise, Object.class);
    }

    @SuppressWarnings("unchecked")
    public final  Promise all(Class type) {
        return all((Promise)this, type);
    }

    @SuppressWarnings("unchecked")
    public final Promise all() {
        return all((Promise)this);
    }

    public final static Promise join(Object...args) {
        return all(args);
    }

    @SuppressWarnings("unchecked")
    public final static  Promise race(Object[] array, Class type) {
        Promise promise = new Promise();
        for (int i = 0, n = array.length; i < n; ++i) {
            ((Promise)toPromise(array[i])).fill(promise);
        }
        return promise;
    }

    public final static Promise race(Object[] array) {
        return race(array, Object.class);
    }

    public final static  Promise race(Promise promise, final Class type) {
        return promise.then(new AsyncFunc() {
            public Promise call(Object[] array) throws Throwable {
                return race(array, type);
            }
        });
    }

    public final static Promise race(Promise promise) {
        return race(promise, Object.class);
    }

    @SuppressWarnings("unchecked")
    public final  Promise race(Class type) {
        return race((Promise)this, type);
    }

    @SuppressWarnings("unchecked")
    public final Promise race() {
        return race((Promise)this);
    }

    @SuppressWarnings("unchecked")
    public final static  Promise any(Object[] array, Class type) {
        int n = array.length;
        if (n == 0) {
            return (Promise)Promise.error(new IllegalArgumentException("any(): array must not be empty"));
        }
        final RuntimeException reason = new RuntimeException("any(): all promises failed");
        final Promise promise = new Promise();
        final AtomicInteger count = new AtomicInteger(n);
        for (int i = 0; i < n; ++i) {
            ((Promise)toPromise(array[i])).then(
                new Action() {
                    public void call(T value) throws Throwable {
                        promise.resolve(value);
                    }
                },
                new Action() {
                    public void call(Throwable e) throws Throwable {
                        if (count.decrementAndGet() == 0) {
                            promise.reject(reason);
                        }
                    }
                }
            );
        }
        return promise;
    }

    public final static Promise any(Object[] array) {
        return any(array, Object.class);
    }

    public final static  Promise any(Promise promise, final Class type) {
        return promise.then(new AsyncFunc() {
            public Promise call(Object[] array) throws Throwable {
                return any(array, type);
            }
        });
    }

    public final static Promise any(Promise promise) {
        return any(promise, Object.class);
    }

    @SuppressWarnings("unchecked")
    public final  Promise any(Class type) {
        return any((Promise)this, type);
    }

    @SuppressWarnings("unchecked")
    public final Promise any() {
        return any((Promise)this);
    }

    public final static Promise run(Action handler, Object...args) {
        return all(args).then(handler);
    }

    public final static  Promise run(Func handler, Object...args) {
        return all(args).then(handler);
    }

    public final static  Promise run(AsyncFunc handler, Object...args) {
        return all(args).then(handler);
    }

    public final static  Promise run(Class type, Action handler, Object...args) {
        return all(args, type).then(handler);
    }

    public final static  Promise run(Class type, Func handler, Object...args) {
        return all(args, type).then(handler);
    }

    public final static  Promise run(Class type, AsyncFunc handler, Object...args) {
        return all(args, type).then(handler);
    }

    @SuppressWarnings("unchecked")
    public final static  Promise forEach(final Action callback, Object...args) {
        return all(args).then(new Action() {
            public void call(Object[] array) throws Throwable {
                if (array == null) return;
                for (int i = 0, n = array.length; i < n; ++i) {
                    callback.call((V)array[i]);
                }
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static  Action getForEachHandler(final Handler callback) {
        return new Action() {
            public void call(Object[] array) throws Throwable {
                if (array == null) return;
                for (int i = 0, n = array.length; i < n; ++i) {
                    callback.call((V)array[i], i);
                }
            }
        };
    }

    public final static  Promise forEach(Object[] array, Handler callback) {
        return all(array).then(getForEachHandler(callback));
    }

    public final static  Promise forEach(Promise array, Handler callback) {
        return all(array).then(getForEachHandler(callback));
    }

    public final Promise forEach(Handler callback) {
        return this.all().then(getForEachHandler(callback));
    }

    @SuppressWarnings("unchecked")
    public final static  Promise every(final Func callback, Object...args) {
        return all(args).then(new Func() {
            public Boolean call(Object[] array) throws Throwable {
                for (int i = 0, n = array.length; i < n; ++i) {
                    if (!callback.call((V)array[i])) return false;
                }
                return true;
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static  Func getEveryHandler(final Handler callback) {
        return new Func() {
            public Boolean call(Object[] array) throws Throwable {
                for (int i = 0, n = array.length; i < n; ++i) {
                    if (!callback.call((V)array[i], i)) return false;
                }
                return true;
            }
        };
    }

    public final static  Promise every(Object[] array, Handler callback) {
        return all(array).then(getEveryHandler(callback));
    }

    public final static  Promise every(Promise array, Handler callback) {
        return all(array).then(getEveryHandler(callback));
    }

    public final  Promise every(Handler callback) {
        return all().then(getEveryHandler(callback));
    }

    @SuppressWarnings("unchecked")
    public final static  Promise some(final Func callback, Object...args) {
        return all(args).then(new Func() {
            public Boolean call(Object[] array) throws Throwable {
                if (array == null) return false;
                for (int i = 0, n = array.length; i < n; ++i) {
                    if (callback.call((V)array[i])) return true;
                }
                return false;
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static  Func getSomeHandler(final Handler callback) {
        return new Func() {
            public Boolean call(Object[] array) throws Throwable {
                if (array == null) return false;
                for (int i = 0, n = array.length; i < n; ++i) {
                    if (callback.call((V)array[i], i)) return true;
                }
                return false;
            }
        };
    }

    public final static  Promise some(Object[] array, Handler callback) {
        return all(array).then(getSomeHandler(callback));
    }

    public final static  Promise some(Promise array, Handler callback) {
        return all(array).then(getSomeHandler(callback));
    }

    public final  Promise some(Handler callback) {
        return this.all().then(getSomeHandler(callback));
    }

    @SuppressWarnings("unchecked")
    public final static  Promise filter(final Func callback, Object...args) {
        return all(args).then(new Func() {
            public Object[] call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                ArrayList result = new ArrayList(n);
                for (int i = 0; i < n; ++i) {
                    if (callback.call((V)array[i])) result.add(array[i]);
                }
                return result.toArray();
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static  Func getFilterHandler(final Handler callback, final Class type) {
        return new Func() {
            public T[] call(T[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                ArrayList result = new ArrayList(n);
                for (int i = 0; i < n; ++i) {
                    if (callback.call((V)array[i], i)) result.add(array[i]);
                }
                return result.toArray((type == Object.class) ?
                (T[])(new Object[result.size()]) :
                (T[])Array.newInstance(type, result.size()));
            }
        };
    }


    public final static  Promise filter(Object[] array, Handler callback, Class type) {
        return all(array, type).then(getFilterHandler(callback, type));
    }

    public final static  Promise filter(Object[] array, Handler callback) {
        return filter(array, callback, Object.class);
    }

    public final static  Promise filter(Promise array, Handler callback, Class type) {
        return all(array, type).then(getFilterHandler(callback, type));
    }

    public final static  Promise filter(Promise array, Handler callback) {
        return filter(array, callback, Object.class);
    }

    public final  Promise filter(Handler callback, Class type) {
        return all(type).then(getFilterHandler(callback, type));
    }

    public final  Promise filter(Handler callback) {
        return filter(callback, Object.class);
    }

    @SuppressWarnings("unchecked")
    public final static  Promise map(final Func callback, Object...args) {
        return all(args).then(new Func() {
            public Object[] call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                Object[] result = new Object[n];
                for (int i = 0; i < n; ++i) {
                    result[i] = callback.call((V)array[i]);
                }
                return result;
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static  Func getMapHandler(final Handler callback, final Class type) {
        return new Func() {
            public T[] call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                T[] result = (type == Object.class) ?
                (T[])(new Object[n]) :
                (T[])Array.newInstance(type, n);
                for (int i = 0; i < n; ++i) {
                    result[i] = callback.call((V)array[i], i);
                }
                return result;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static  Func getMapHandler(final Handler callback) {
        return new Func() {
            public Object[] call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                Object[] result = new Object[n];
                for (int i = 0; i < n; ++i) {
                    result[i] = callback.call((V)array[i], i);
                }
                return result;
            }
        };
    }

    public final static  Promise map(Object[] array, Handler callback, Class type) {
        return all(array).then(getMapHandler(callback, type));
    }

    public final static  Promise map(Object[] array, Handler callback) {
        return all(array).then(getMapHandler(callback));
    }

    public final static  Promise map(Promise array, Handler callback, Class type) {
        return all(array).then(getMapHandler(callback, type));
    }

    public final static  Promise map(Promise array, Handler callback) {
        return all(array).then(getMapHandler(callback));
    }

    public final  Promise map(Handler callback, Class type) {
        return all().then(getMapHandler(callback, type));
    }

    public final  Promise map(Handler callback) {
        return all().then(getMapHandler(callback));
    }


    @SuppressWarnings("unchecked")
    private static  Func getReduceHandler(final Reducer callback) {
        return new Func() {
            public V call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                if (n == 0) return null;
                V result = (V)array[0];
                for (int i = 1; i < n; ++i) {
                    result = callback.call(result, (V)array[i], i);
                }
                return result;
            }
        };
    }

    public final static  Promise reduce(Object[] array, Reducer callback) {
        return all(array).then(getReduceHandler(callback));
    }

    public final static  Promise reduce(Promise array, Reducer callback) {
        return all(array).then(getReduceHandler(callback));
    }

    public final  Promise reduce(Reducer callback) {
        return all().then(getReduceHandler(callback));
    }

    @SuppressWarnings("unchecked")
    private static  Func getReduceHandler(final Reducer callback, final R initialValue) {
        return new Func() {
            public R call(Object[] array) throws Throwable {
                if (array == null) return initialValue;
                int n = array.length;
                if (n == 0) return initialValue;
                R result = initialValue;
                for (int i = 0; i < n; ++i) {
                    result = callback.call(result, (V)array[i], i);
                }
                return result;
            }
        };
    }

    public final static  Promise reduce(Object[] array, Reducer callback, R initialValue) {
        return all(array).then(getReduceHandler(callback, initialValue));
    }

    public final static  Promise reduce(Promise array, Reducer callback, R initialValue) {
        return all(array).then(getReduceHandler(callback, initialValue));
    }

    public final  Promise reduce(Reducer callback, R initialValue) {
        return all().then(getReduceHandler(callback, initialValue));
    }

    @SuppressWarnings("unchecked")
    private static  Func getReduceRightHandler(final Reducer callback) {
        return new Func() {
            public V call(Object[] array) throws Throwable {
                if (array == null) return null;
                int n = array.length;
                if (n == 0) return null;
                V result = (V)array[n - 1];
                for (int i = n - 2; i >= 0; --i) {
                    result = callback.call(result, (V)array[i], i);
                }
                return result;
            }
        };
    }

    public final static  Promise reduceRight(Object[] array, Reducer callback) {
        return all(array).then(getReduceRightHandler(callback));
    }

    public final static  Promise reduceRight(Promise array, Reducer callback) {
        return all(array).then(getReduceRightHandler(callback));
    }

    public final  Promise reduceRight(Reducer callback) {
        return all().then(getReduceRightHandler(callback));
    }

    @SuppressWarnings("unchecked")
    private static  Func getReduceRightHandler(final Reducer callback, final R initialValue) {
        return new Func() {
            public R call(Object[] array) throws Throwable {
                if (array == null) return initialValue;
                int n = array.length;
                if (n == 0) return initialValue;
                R result = initialValue;
                for (int i = n - 1; i >= 0; --i) {
                    result = callback.call(result, (V)array[i], i);
                }
                return result;
            }
        };
    }

    public final static  Promise reduceRight(Object[] array, Reducer callback, R initialValue) {
        return all(array).then(getReduceRightHandler(callback, initialValue));
    }

    public final static  Promise reduceRight(Promise array, Reducer callback, R initialValue) {
        return all(array).then(getReduceRightHandler(callback, initialValue));
    }

    public final  Promise reduceRight(Reducer callback, R initialValue) {
        return all().then(getReduceRightHandler(callback, initialValue));
    }

    @SuppressWarnings("unchecked")
    private  void call(final Callback callback, final Promise next, final V x) {
        try {
            if (callback instanceof Action) {
                ((Action)callback).call(x);
                next.resolve((R)null);
            }
            else if (callback instanceof Func) {
                next.resolve((R)((Func)callback).call(x));
            }
            else if (callback instanceof AsyncFunc) {
                next.resolve((Promise)((AsyncFunc)callback).call(x));
            }
        }
        catch (Throwable e) {
            next.reject(e);
        }
    }

    @SuppressWarnings("unchecked")
    private  void resolve(final Callback onfulfill, final Promise next, final V x) {
        if (onfulfill != null) {
            call(onfulfill, next, x);
        }
        else {
            next.resolve((R)x);
        }
    }

    private  void reject(final Callback onreject, final Promise next, final Throwable e) {
        if (onreject != null) {
            call(onreject, next, e);
        }
        else {
            next.reject(e);
        }
    }

    @SuppressWarnings("unchecked")
    private synchronized  void _resolve(V value) {
        if (state.compareAndSet(State.PENDING, State.FULFILLED)) {
            this.value = value;
            while (!subscribers.isEmpty()) {
                Subscriber subscriber = (Subscriber)subscribers.poll();
                resolve(subscriber.onfulfill, subscriber.next, value);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public final void resolve(Object value) {
        if (isPromise(value)) {
            resolve((Promise)value);
        }
        else if (isThenable(value)) {
            resolve((Thenable)value);
        }
        else {
            _resolve((V)value);
        }
    }

    public final void resolve(Thenable value) {
        final AtomicBoolean notrun = new AtomicBoolean(true);
        Action resolveFunction = new Action() {
            public void call(V y) throws Throwable {
                if (notrun.compareAndSet(true, false)) {
                    resolve(y);
                }
            }
        };
        Action rejectFunction = new Action() {
            public void call(Throwable e) throws Throwable {
                if (notrun.compareAndSet(true, false)) {
                    reject(e);
                }
            }
        };
        try {
            value.then(resolveFunction, rejectFunction);
        }
        catch (Throwable e) {
            if (notrun.compareAndSet(true, false)) {
                reject(e);
            }
        }
    }

    public final void resolve(Promise value) {
        if (value == null) {
            _resolve(null);
        }
        else if (value == this) {
            reject(new TypeException("Self resolution"));
        }
        else {
            value.fill(this);
        }
    }

    @SuppressWarnings("unchecked")
    private synchronized  void _reject(Throwable e) {
        if (state.compareAndSet(State.PENDING, State.REJECTED)) {
            this.reason = e;
            while (!subscribers.isEmpty()) {
                Subscriber subscriber = (Subscriber)subscribers.poll();
                reject(subscriber.onreject, subscriber.next, e);
            }
        }
    }

    public final void reject(Throwable e) {
        _reject(e);
    }

    public final Promise then(Action onfulfill) {
        return then(onfulfill, null);
    }

    public final  Promise then(Func onfulfill) {
        return then((Callback)onfulfill, null);
    }

    public final  Promise then(AsyncFunc onfulfill) {
        return then((Callback)onfulfill, null);
    }

    public final Promise then(Action onfulfill, Action onreject) {
        return then((Callback)onfulfill, (Callback)onreject);
    }

    public final  Promise then(Func onfulfill, Func onreject) {
        return then((Callback)onfulfill, (Callback)onreject);
    }

    public final  Promise then(AsyncFunc onfulfill, Func onreject) {
        return then((Callback)onfulfill, (Callback)onreject);
    }

    public final  Promise then(AsyncFunc onfulfill, AsyncFunc onreject) {
        return then((Callback)onfulfill, (Callback)onreject);
    }

    public final  Promise then(Func onfulfill, AsyncFunc onreject) {
        return then((Callback)onfulfill, (Callback)onreject);
    }

    @SuppressWarnings("unchecked")
    private synchronized  Promise then(Callback onfulfill, Callback onreject) {
        Promise next = new Promise();
        switch (state.get()) {
            case FULFILLED:
                resolve(onfulfill, next, value);
                break;
            case REJECTED:
                reject(onreject, next, reason);
                break;
            default:
                subscribers.offer(new Subscriber(onfulfill, onreject, next));
                break;
        }
        return next;
    }

    public final void done(Action onfulfill) {
        done(onfulfill, null);
    }

    public final void done(Action onfulfill, Action onreject) {
         then(onfulfill, onreject).then(null, new Action() {
            public void call(final Throwable e) {
                timer.execute(new Runnable() {
                    public void run() {
                        throw new RuntimeException(e);
                    }
                });
            }
        });
    }

    public final State getState() {
        return state.get();
    }

    public final V getValue() {
        return value;
    }

    public final Throwable getReason() {
        return reason;
    }

    public final Promise catchError(Action onreject) {
        return then(null, onreject);
    }

    public final  Promise catchError(Func onreject) {
        return then((Callback)null, onreject);
    }

    public final  Promise catchError(AsyncFunc onreject) {
        return then((Callback)null, onreject);
    }

    public final Promise catchError(Action onreject, Func test) {
        return catchError((Callback)onreject, test);
    }

    public final  Promise catchError(Func onreject, Func test) {
        return catchError((Callback)onreject, test);
    }

    public final  Promise catchError(AsyncFunc onreject, Func test) {
        return catchError((Callback)onreject, test);
    }

    public final Promise catchError(Action onreject, AsyncFunc test) {
        return catchError((Callback)onreject, test);
    }

    public final  Promise catchError(Func onreject, AsyncFunc test) {
        return catchError((Callback)onreject, test);
    }

    public final  Promise catchError(AsyncFunc onreject, AsyncFunc test) {
        return catchError((Callback)onreject, test);
    }

    @SuppressWarnings("unchecked")
    private  Promise catchError(final Callback onreject, final Func test) {
        if (test != null) {
            return then((Callback)null, new AsyncFunc() {
                public Promise call(Throwable e) throws Throwable {
                    if (test.call(e)) {
                        return then(null, onreject);
                    }
                    throw e;
                }
            });
        }
        return then(null, onreject);
    }

    @SuppressWarnings("unchecked")
    private  Promise catchError(final Callback onreject, final AsyncFunc test) {
        if (test != null) {
            return then((Callback)null, new AsyncFunc() {
                public Promise call(final Throwable e) throws Throwable {
                    return test.call(e).then(new AsyncFunc() {
                        public Promise call(Boolean value) throws Throwable {
                            if (value) {
                                return then(null, onreject);
                            }
                            throw e;
                        }
                    });
                }
            });
        }
        return then(null, onreject);
    }

    public final void fail(Action onreject) {
        done(null, onreject);
    }

    public final Promise whenComplete(final Runnable action) {
        return then(
            new Func() {
                public V call(final V value) throws Throwable {
                    action.run();
                    return value;
                }
            },
            new Func() {
                public V call(final Throwable e) throws Throwable {
                    action.run();
                    throw e;
                }
            }
        );
    }

    public final Promise whenComplete(final Action action) {
        return then(
            new Func() {
                @SuppressWarnings("unchecked")
                public V call(final V value) throws Throwable {
                   ((Action)action).call(value);
                    return value;
                }
            },
            new Func() {
                @SuppressWarnings("unchecked")
                public V call(final Throwable e) throws Throwable {
                    ((Action)action).call(e);
                    throw e;
                }
            }
        );
    }

    @SuppressWarnings("unchecked")
    public final Promise complete(Action oncomplete) {
        return then((Action)oncomplete, (Action)oncomplete);
    }

    @SuppressWarnings("unchecked")
    public final  Promise complete(Func oncomplete) {
        return then((Func)oncomplete, (Func)oncomplete);
    }

    @SuppressWarnings("unchecked")
    public final  Promise complete(AsyncFunc oncomplete) {
        return then((AsyncFunc)oncomplete, (AsyncFunc)oncomplete);
    }

    @SuppressWarnings("unchecked")
    public final void always(Action oncomplete) {
        done((Action)oncomplete, (Action)oncomplete);
    }

    public final void fill(final Promise promise) {
        then(
            new Action() {
                public void call(V value) throws Throwable {
                    promise.resolve(value);
                }
            },
            new Action() {
                public void call(Throwable e) throws Throwable {
                    promise.reject(e);
                }
            }
        );
    }

    public final Promise timeout(long duration, TimeUnit timeunit, final Throwable reason) {
        final Promise promise = new Promise();
        final Future timeoutID = timer.schedule(new Runnable() {
            public void run() {
                if (reason == null) {
                    promise.reject(new TimeoutException("timeout"));
                }
                else {
                    promise.reject(reason);
                }
            }
        }, duration, timeunit);
        whenComplete(new Runnable() {
            public void run() {
                timeoutID.cancel(true);
            }
        }).fill(promise);
        return promise;
    }

    public final Promise timeout(long duration, Throwable reason) {
        return timeout(duration, TimeUnit.MILLISECONDS, reason);
    }

    public final Promise timeout(long duration) {
        return timeout(duration, TimeUnit.MILLISECONDS, null);
    }

    public final Promise delay(final long duration, final TimeUnit timeunit) {
        final Promise promise = new Promise();
        then(new Action() {
                public void call(final V value) throws Throwable {
                    timer.schedule(new Runnable() {
                        public void run() {
                            promise.resolve(value);
                        }
                    }, duration, timeunit);
                }
            },
            new Action() {
                public void call(Throwable e) throws Throwable {
                    promise.reject(e);
                }
            }
        );
        return promise;
    }

    public final Promise delay(long duration) {
        return delay(duration, TimeUnit.MILLISECONDS);
    }

    @SuppressWarnings("unchecked")
    public final Promise tap(final Action onfulfilledSideEffect) {
        return then(new Func() {
            public V call(V value) throws Throwable {
                onfulfilledSideEffect.call(value);
                return value;
            }
        });
    }

    public final Future toFuture() {
        return new PromiseFuture(this);
    }
}