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

com.englishtown.promises.internal.ArrayHelper Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
package com.englishtown.promises.internal;

import com.englishtown.promises.Promise;
import com.englishtown.promises.PromiseResolver;
import com.englishtown.promises.State;
import com.englishtown.promises.Thenable;
import com.englishtown.promises.exceptions.RejectException;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Helper methods for array promise operations
 */
public class ArrayHelper {

    private final PromiseHelper helper;

    @Inject
    public ArrayHelper(PromiseHelper helper) {
        this.helper = helper;
    }

    /**
     * One-winner competitive race.
     * Return a promise that will fulfill when one of the promises
     * in the input array fulfills, or will reject when all promises
     * have rejected.
     *
     * @param promises list of promises
     * @param       type of promises
     * @return {Promise} promise for the first fulfilled value
     */
    public  Promise any(List> promises) {

        PromiseResolver resolver = (resolve, reject) -> {
            ValueHolder pending = new ValueHolder<>(promises.size());
            List errors = new ArrayList<>();

            Function> handleResolve = (x) -> {
                resolve.accept(x);
                return null;
            };

            Function> handleReject = (e) -> {
                errors.add(e);
                if (--pending.value == 0) {
                    reject.accept(new RejectException("All promises rejected", errors));
                }
                return null;
            };

            promises.forEach((p) -> {
                helper.toPromise(p).then(handleResolve, handleReject);
            });

            if (pending.value == 0) {
                resolve.accept(null);
            }

        };

        return new TrustedPromise<>(resolver, helper);
    }

    /**
     * N-winner competitive race
     * Return a promise that will fulfill when n input promises have
     * fulfilled, or will reject when it becomes impossible for n
     * input promises to fulfill (ie when promises.length - n + 1
     * have rejected)
     *
     * @param promises list of promises
     * @param n        number of promises to fulfill
     * @param       type of promises
     * @return promise for the earliest n fulfillment values
     */
    public  Promise> some(List> promises, int n) {

        int nFinal = Math.max(n, 0);

        return new TrustedPromise<>((resolve, reject) -> {
            final ValueHolder nFulfill = new ValueHolder<>(0);
            final ValueHolder nReject = new ValueHolder<>(null);
            List results = new ArrayList<>(nFinal);
            List errors = new ArrayList<>();

            Function> handleResolve = (x) -> {
                if (nFulfill.value > 0) {
                    --nFulfill.value;
                    results.add(x);

                    if (nFulfill.value == 0) {
                        resolve.accept(results);
                    }
                }
                return null;
            };

            Function> handleReject = (e) -> {
                if (nReject.value > 0) {
                    --nReject.value; // TODO: sync?
                    errors.add(e);

                    if (nReject.value == 0) {
                        reject.accept(new RejectException("Too many rejections", errors));
                    }
                }
                return null;
            };

            nReject.value = (promises.size() - nFinal + 1);
            nFulfill.value = Math.min(nFinal, promises.size());

            if (nFulfill.value == 0) {
                resolve.accept(results);
                return;
            }

            promises.stream().forEach(p -> {
                helper.toPromise(p).then(handleResolve, handleReject);
            });

        }, helper);
    }

    /**
     * Apply f to the value of each promise in a list of promises
     * and return a new list containing the results.
     *
     * @param promises list of promises
     * @param f        function run when a promise fulfills
     * @param fallback function run when a promise rejects
     * @param       type of promises
     * @return promise for list of results
     */
    public  Promise> map(List> promises, Function> f, Function> fallback) {

        return helper.all(promises
                .stream()
                .map(x -> helper.toPromise(x).then(f, fallback))
                .collect(Collectors.toList()));

    }

    /**
     * Return a promise that will always fulfill with an array containing
     * the outcome states of all input promises.  The returned promise
     * will never reject.
     *
     * @param promises list of promises
     * @param       type of promises
     * @return promise for list of states
     */
    public  Promise>> settle(List> promises) {

        return helper.all(promises.stream().map(p -> {
            TrustedPromise p1 = helper.toPromise(p);
            //noinspection unchecked
            return p1.then(
                    x -> helper.resolve(p1.inspect()),
                    t -> helper.resolve(p1.inspect())
            );
        }).collect(Collectors.toList()));

    }

    public  Promise reduce(List> promises, BiFunction> f) {

        //noinspection unchecked
        List> thenables = (List>) promises;

        return (Promise) thenables
                .stream()
                .reduce(
                        (result, x) -> helper.toPromise(result).then(
                                r -> helper.toPromise(x).then(
                                        x1 -> f.apply(r, x1)
                                )
                        ))
                .get();

    }

    public  Promise reduce(List> promises, BiFunction> f, Thenable initialValue) {

        //noinspection unchecked
        List> thenables = (List>) promises;

        return (Promise) thenables
                .stream()
                .reduce(
                        initialValue,
                        (result, x) -> helper.toPromise(result).then(
                                r -> helper.toPromise(x).then(
                                        x1 -> f.apply(r, x1)
                                )
                        ), (c1, c2) -> c1);

    }

}