import ceylon.promise.internal { AtomicRef } "The deferred class is the primary implementation of the [[Promise]] interface. The promise is accessible using the `promise` attribute of the deferred. The deferred can either be fulfilled or rejected via the [[Completable.fulfill]] or [[Completable.reject]] methods. Both methods accept an argument or a promise to the argument, allowing the deferred to react on a promise." by("Julien Viet") shared class Deferred<Value>(context = globalExecutionContext) satisfies Completable<Value> & Promised<Value> { abstract class State() of ListenerState | PromiseState {} class PromiseState(shared Promise<Value> promise) extends State() {} class ListenerState(onFulfilled, onRejected, ListenerState? previous = null) extends State() { Anything(Value) onFulfilled; Anything(Throwable) onRejected; shared void update(Promise<Value> promise) { if (exists previous) { previous.update(promise); } promise.map(onFulfilled, onRejected); } } "The current context" ExecutionContext context; "The current state" value state = AtomicRef<State?>(null); "The promise of this deferred." shared actual object promise extends Promise<Value>() { context => outer.context; shared actual Promise<Result> map<Result>( Result(Value) onFulfilled, Result(Throwable) onRejected) { ExecutionListener[] listeners = currentExecutionListeners.get(); Anything(Anything())[] wrappers; if (!listeners.empty) { wrappers = [ * listeners.map((ExecutionListener listener) => listener.onChild()) ]; } else { wrappers =[]; } value childContext = context.childContext(); value deferred = Deferred<Result>(childContext); void callback<T>(Result(T) on, T val) { variable Anything() task = () { Result t; try { t = on(val); } catch (Throwable e) { deferred.reject(e); return; } deferred.fulfill(t); }; if (!listeners.empty) { for (wrapper in wrappers) { Anything() f = task; task = void() { wrapper(f); }; } } context.run(task); } foobar { void onFulfilledCallback(Value val) => callback(onFulfilled, val); void onRejectedCallback(Throwable reason) => callback(onRejected, reason); }; // return deferred.promise; } shared actual Promise<Result> flatMap<Result>( Promise<Result>(Value) onFulfilled, Promise<Result>(Throwable) onRejected) { ExecutionListener[] listeners = currentExecutionListeners.get(); Anything(Anything())[] wrappers; if (!listeners.empty) { wrappers = [ * listeners.map((ExecutionListener listener) => listener.onChild()) ]; } else { wrappers =[]; } value childContext = context.childContext(); value deferred = Deferred<Result>(childContext); void callback<T>(Promise<Result>(T) on, T val) { variable Anything() task = void () { Promise<Result> t; try { t = on(val); } catch (Throwable e) { deferred.reject(e); return; } deferred.fulfill(t); }; if (!listeners.empty) { for (wrapper in wrappers) { Anything() f = task; task = void() { wrapper(f); }; } } context.run(task); } foobar { void onFulfilledCallback(Value val) => callback(onFulfilled, val); void onRejectedCallback(Throwable reason) => callback(onRejected, reason); }; // return deferred.promise; } void foobar(void onFulfilledCallback(Value val), void onRejectedCallback(Throwable reason)) { while (true) { value current = state.get(); switch (current) case (is Null) { State next = ListenerState(onFulfilledCallback, onRejectedCallback); if (state.compareAndSet(current, next)) { break; } } case (is ListenerState) { State next = ListenerState(onFulfilledCallback, onRejectedCallback, current); if (state.compareAndSet(current, next)) { break; } } case (is PromiseState) { current.promise.map(onFulfilledCallback, onRejectedCallback); break; } } } } void update(Promise<Value> promise) { while (true) { value current = state.get(); switch (current) case (is Null) { PromiseState next = PromiseState(promise); if (state.compareAndSet(current, next)) { break; } } case (is ListenerState) { PromiseState next = PromiseState(promise); if (state.compareAndSet(current, next)) { current.update(promise); break; } } case (is PromiseState) { break; } } } shared actual void fulfill(Value|Promise<Value> val) { if (is Promise<Value> val) { update(val); } else { update(context.fulfilledPromise<Value>(val)); } } reject(Throwable reason) => update(context.rejectedPromise<Value>(reason)); }