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

sirius.kernel.async.Promises Maven / Gradle / Ivy

/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.kernel.async;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * Provides utility functions when dealing with {@link Promise promises}.
 */
public class Promises {

    private Promises() {
    }

    /**
     * Iterates over the given input and invokes an async function for each element.
     * 

* Note that this is done sequentially, therefore only one call happens at a time * * @param items the items to iterate over * @param toPromiseHandler the async function which returns a promise to indicate completion * @param resultConsumer the handler used to collect the results * @param the input type * @param the output type generated by the async function * @return a future which is fulfilled once all items have been processer or failed if one item fails */ public static Future processChain(Iterable items, Function> toPromiseHandler, BiConsumer resultConsumer) { Future result = new Future(); processChain(items.iterator(), toPromiseHandler, resultConsumer, result); return result; } private static void processChain(Iterator iter, Function> toPromiseHandler, BiConsumer resultConsumer, Future completionFuture) { if (!iter.hasNext()) { completionFuture.success(); return; } I item = iter.next(); toPromiseHandler.apply(item).onSuccess(result -> { resultConsumer.accept(item, result); processChain(iter, toPromiseHandler, resultConsumer, completionFuture); }).onFailure(completionFuture::fail); } /** * Transforms a collection of items into a promise for a list of results while invoking an async function for * each item. * * @param input the items to iterate over * @param toPromiseHandler the async function which returns a promise to indicate completion * @param the input type * @param the output type generated by the async function * @return a promise containing the invocation results of the async function for each item in the input */ public static Promise> sequence(Iterable input, Function> toPromiseHandler) { Promise> result = new Promise<>(); List buffer = new ArrayList<>(); processChain(input, toPromiseHandler, (ignored, output) -> buffer.add(output)).onSuccess(() -> result.success( buffer)).onFailure(result::fail); return result; } /** * Turns a list of promises into a promise for a list of values. *

* Note that all values need to have the same type. *

* If only the completion of all promises matters in contrast to their actual result, a {@link Barrier} can also * be used. This also permits to wait for promises of different types. * * @param list the list of promises to convert. * @param the type of each promise. * @return the promise which will complete if all promises completed or if at least on failed. */ public static Promise> parallel(List> list) { final Promise> result = new Promise<>(); // Create a list with the correct length final List resultList = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { resultList.add(null); } // Keep track when we're finished final CountDownLatch latch = new CountDownLatch(list.size()); // Iterate over all promises and create a completion handler, which either forwards a failure or which placesy // a successfully computed in the created result list int index = 0; for (Promise promise : list) { final int currentIndex = index; promise.onComplete(new CompletionHandler() { @Override public void onSuccess(@Nullable V value) throws Exception { if (!result.isFailed()) { // onSuccess can be called from any thread -> sync on resultList... synchronized (resultList) { resultList.set(currentIndex, value); } // Keep track how many results we're waiting for and forward the result when we're finished. latch.countDown(); if (latch.getCount() <= 0) { result.success(resultList); } } } @Override public void onFailure(Throwable throwable) throws Exception { result.fail(throwable); } }); index++; } return result; } }