Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.englishtown.promises.internal.PromiseHelper Maven / Gradle / Ivy
package com.englishtown.promises.internal;
import com.englishtown.promises.*;
import com.englishtown.promises.internal.handlers.*;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Injected promise helper methods
*/
public class PromiseHelper {
private final Scheduler scheduler;
private final Context context;
private final Reporter reporter;
private final Promise foreverPendingPromise;
@Inject
public PromiseHelper(Environment environment, Context context, Reporter reporter) {
this.scheduler = environment.getScheduler();
this.context = context;
this.reporter = reporter;
Handler foreverPendingHandler = new Handler(this) {
};
foreverPendingPromise = new TrustedPromise<>(foreverPendingHandler, this);
}
/**
* Returns a trusted promise.
*
* @param x a value to be wrapped in a promise
* @param type of resolved value
* @return {Promise} promise
*/
public TrustedPromise resolve(T x) {
return resolve0(x);
}
/**
* Returns a trusted promise. If x is already a trusted promise, it is
* returned, otherwise returns a new trusted Promise which follows x.
*
* @param x a thenable to be wrapped in a trusted promise
* @param type of thenable to resolve with
* @return {Promise} promise
*/
public TrustedPromise resolve(Thenable x) {
return resolve0(x);
}
@SuppressWarnings("unchecked")
private TrustedPromise resolve0(Object x) {
return isPromise(x) ? (TrustedPromise) x
: new TrustedPromise<>(new AsyncHandler<>(this.getHandler(x), this), this);
}
TrustedPromise toPromise(T x) {
return resolve0(x);
}
TrustedPromise toPromise(Thenable x) {
return resolve0(x);
}
// TODO: need both resolve and toPromise?
/**
* Return a rejected promise with x as its reason (x is used verbatim)
*
* @param x a throwable to reject with
* @param type of rejected promise
* @return {Promise} rejected promise
*/
public TrustedPromise reject(Throwable x) {
return new TrustedPromise<>(new AsyncHandler<>(new RejectedHandler(x, this), this), this);
}
@SuppressWarnings("unchecked")
public Promise never() {
return (Promise) foreverPendingPromise;
}
/**
* Creates an internal {promise, resolver} pair
*
* @param type of deferred
* @return {Promise}
*/
public TrustedPromise defer() {
return new TrustedPromise<>(new DeferredHandler<>(this, null), this);
}
/**
* Get an appropriate handler for x, without checking for cycles
*
* @param x promise, thenable, or fulfillment value
* @param type of handler
* @return {object} handler
*/
@SuppressWarnings("unchecked")
public Handler getHandler(Object x) {
if (isPromise(x)) {
return ((TrustedPromise) x)._handler.join();
}
return maybeThenable(x) ? getHandlerUntrusted((Thenable) x) : new FulfilledHandler<>((T) x, this);
}
public boolean isPromise(Object x) {
return x instanceof TrustedPromise;
}
/**
* @param x object to check if thenable
* @return {boolean} false iff x is guaranteed not to be a thenable
*/
public boolean maybeThenable(Object x) {
return x instanceof Thenable;
}
/**
* Get a handler for potentially untrusted thenable x
*
* @param x a thenable
* @return {object} handler
*/
private Handler getHandlerUntrusted(Thenable x) {
try {
return (x != null) ? new ThenableHandler<>(x, this) : new FulfilledHandler<>(null, this);
// var untrustedThen = x.then;
// return typeof untrustedThen === 'function'
// ? new ThenableHandler(untrustedThen, x)
// : new FulfilledHandler(x);
} catch (Throwable e) {
return new RejectedHandler<>(e, this);
}
}
/**
* Return f.call(thisArg, x), or if it throws return a rejected promise for
* the thrown exception
*
* @param f function to apply in try/catch
* @param x parameter passed to f
* @param type of parameter
* @param type of thenable returned
* @return result of f or rejected promise if exception thrown
*/
public Thenable tryCatchReject(Function> f, T x) {
try {
return resolve0(f.apply(x));
} catch (Throwable e) {
return reject(e);
}
}
/**
* Same as above, but includes the extra argument parameter.
*
* @param f function to apply in try/catch
* @param x parameter passed to f
* @param y second parameter passed to f
* @param type of parameter
* @param type of second parameter
* @param type of thenable returned
* @return result of f or rejected promise if exception thrown
*/
public Thenable tryCatchReject2(BiFunction> f, T x, U y) {
try {
return resolve0(f.apply(x, y));
} catch (Throwable e) {
return reject(e);
}
}
public Scheduler getScheduler() {
return this.scheduler;
}
public Context getContext() {
return this.context;
}
public Reporter getReporter() {
return this.reporter;
}
/**
* Return a promise that will fulfill when all promises in the
* input array have fulfilled, or will reject when one of the
* promises rejects.
*
* @param promises array of promises
* @param type of promises
* @return {Promise} promise for array of fulfillment values
*/
public Promise> all(List extends Thenable> promises) {
DeferredHandler> resolver = new DeferredHandler<>(this, null);
ValueHolder pending = new ValueHolder<>(promises == null ? 0 : promises.size());
List results = new ArrayList<>(pending.value);
BiConsumer, Integer> resolveOne = (handler, i) -> {
handler.map((x) -> {
results.set(i, x);
if (--(pending.value) == 0) {
resolver.become(new FulfilledHandler<>(results, this));
}
}, resolver);
};
if (promises == null) {
throw new IllegalArgumentException("promises cannot be null");
}
for (int i = 0; i < promises.size(); ++i) {
Thenable x = promises.get(i);
results.add(i, null);
if (x == null) {
--pending.value;
continue;
}
Handler h = isPromise(x)
? ((TrustedPromise) x)._handler.join()
: getHandlerUntrusted(x);
HandlerState s = h.state();
switch (s) {
case PENDING:
resolveOne.accept((DeferredHandler) h, i);
break;
case FULFILLED:
// TODO: Use inspect instead?
results.set(i, ((FulfilledHandler) h).getValue());
--pending.value;
break;
case REJECTED:
// TODO: finish all() rejected. Better approach? inspect()?
resolver.reject(((RejectedHandler) h).getValue());
// resolver.become(h);
// Break the for loop
i = promises.size();
break;
}
// TODO: Support values or only promises?
// if (helper.maybeThenable(x)) {
//
// } else {
// results[i] = x;
// --pending;
// }
}
if (pending.value == 0) {
resolver.become(new FulfilledHandler<>(results, this));
}
return new TrustedPromise<>(resolver, this);
}
/**
* Fulfill-reject competitive race. Return a promise that will settle
* to the same state as the earliest input promise to settle.
*
* WARNING: The ES6 Promise spec requires that race()ing an empty array
* must return a promise that is pending forever. This implementation
* returns a singleton forever-pending promise, the same singleton that is
* returned by Promise.never(), thus can be checked with ===
*
* @param promises array of promises to race
* @param type of promises
* @return {Promise} if input is non-empty, a promise that will settle
* to the same outcome as the earliest input promise to settle. if empty
* is empty, returns a promise that will never settle.
*/
public Promise race(List extends Thenable> promises) {
// Sigh, race([]) is untestable unless we return *something*
// that is recognizable without calling .then() on it.
if (promises.size() == 0) {
return never();
}
DeferredHandler h = new DeferredHandler<>(this, null);
for (int i = 0; i < promises.size(); ++i) {
Thenable x = promises.get(i);
if (x != null) {
this.getHandler(x).chain(h::resolve, h::reject);
}
}
return new TrustedPromise<>(h, this);
}
}