AbstractPromise implements Promise<T> { @NotNull @Override public Promise<T> whenResult(@NotNull Consumer<? super T> action) { if (isComplete()) { if (isResult()) action.accept(result); return this; } subscribe(new SimpleCallback<T>() { @Override public void accept(T result, @Nullable Throwable e) { if (e == null) { action.accept(result); } } @Override public String toString() { return ".whenResult(" + formatToString(action) + ')'; } }); return this; }  void reset(); void resetCallbacks(); @Override final boolean isComplete(); @Override final boolean isResult(); @Override final boolean isException(); @Override T getResult(); @Override Throwable getException(); @Override Try<T> getTry(); @NotNull @Override Promise<U> next(@Async.Schedule @NotNull P promise); @NotNull @Override Promise<U> map(@NotNull Function<? super T, ? extends U> fn); @NotNull @Override Promise<U> mapEx(@NotNull BiFunction<? super T, Throwable, ? extends U> fn); @NotNull @Override Promise<U> then(@NotNull Function<? super T, ? extends Promise<? extends U>> fn); @Override @NotNull Promise<U> then(@NotNull Supplier<? extends Promise<? extends U>> fn); @NotNull @Override Promise<U> thenEx(@NotNull BiFunction<? super T, Throwable, ? extends Promise<? extends U>> fn); @NotNull @Override Promise<T> whenComplete(@NotNull Callback<? super T> action); @NotNull @Override Promise<T> whenComplete(@NotNull Runnable action); @NotNull @Override Promise<T> whenResult(@NotNull Consumer<? super T> action); @Override Promise<T> whenResult(@NotNull Runnable action); @Override Promise<T> whenException(@NotNull Consumer<Throwable> action); @Override Promise<T> whenException(@NotNull Runnable action); @NotNull @Override Promise<T> async(); @NotNull @Override Promise<V> combine(@NotNull Promise<? extends U> other, @NotNull BiFunction<? super T, ? super U, ? extends V> fn); @NotNull @Override Promise<Void> both(@NotNull Promise<?> other); @NotNull @Override Promise<T> either(@NotNull Promise<? extends T> other); @NotNull @Override Promise<Try<T>> toTry(); @NotNull @Override Promise<Void> toVoid(); @NotNull @Override CompletableFuture<T> toCompletableFuture(); @Override String toString();  }
@Test public void testRecursionProblems() { int count = 1_000_000; SettablePromise<Void> settablePromise = new SettablePromise<>(); RefInt refInt = new RefInt(count); for (int i = 0; i < count; i++) { settablePromise.whenResult(() -> refInt.dec()); } settablePromise.set(null); assertEquals(0, refInt.get()); }