internal void _cloneResult(_Future source) { D.assert(!_isComplete); D.assert(source._isComplete); _state = source._state; _resultOrListeners = source._resultOrListeners; }
public static Future sync(Func <FutureOr> computation) { try { var result = computation(); if (result.isFuture) { return(result.f); } else { return(_Future.value(result)); } } catch (Exception error) { var future = new _Future(); AsyncError replacement = Zone.current.errorCallback(error); if (replacement != null) { future._asyncCompleteError(async_._nonNullError(replacement.InnerException)); } else { future._asyncCompleteError(error); } return(future); } }
static void _chainForeignFuture(Future source, _Future target) { D.assert(!target._isComplete); D.assert(!(source is _Future)); // Mark the target as chained (and as such half-completed). target._setPendingComplete(); try { source.then((value) => { D.assert(target._isPendingComplete); // The "value" may be another future if the foreign future // implementation is mis-behaving, // so use _complete instead of _completeWithValue. target._clearPendingComplete(); // Clear this first, it's set again. target._complete(FutureOr.value(value)); return(new FutureOr()); }, onError: (Exception error) => { D.assert(target._isPendingComplete); target._completeError(error); return(new FutureOr()); }); } catch (Exception e) { // This only happens if the `then` call threw synchronously when given // valid arguments. // That requires a non-conforming implementation of the Future interface, // which should, hopefully, never happen. async_.scheduleMicrotask(() => { target._completeError(e); return(null); }); } }
internal static _Future immediateError(Exception error) { var future = new _Future(Zone.current); future._asyncCompleteError(error); return(future); }
Future <string> join(string separator = "") { _Future result = new _Future(); StringBuilder buffer = new StringBuilder(); StreamSubscription <T> subscription = null; bool first = true; subscription = listen( (T element) => { if (!first) { buffer.Append(separator); } first = false; try { buffer.Append(element); } catch (Exception e) { _stream._cancelAndErrorWithReplacement(subscription, result, e); } }, onError: (e, _) => result._completeError((Exception)e), onDone: () => { result._complete(buffer.ToString()); }, cancelOnError: true); return(result.to <string>()); }
internal static _Future zoneValue(object value, Zone zone) { var future = new _Future(zone); future._setValue(value); return(future); }
internal static _Future immediate(FutureOr result) { var future = new _Future(Zone.current); future._asyncComplete(result); return(future); }
public static _FutureListener whenComplete(_Future result, _FutureAction callback) { return(new _FutureListener( result, callback, null, stateWhencomplete )); }
internal void _addListener(_FutureListener listener) { D.assert(listener._nextListener == null); if (_mayAddListener) { listener._nextListener = (_FutureListener)_resultOrListeners; _resultOrListeners = listener; } else { if (_isChained) { // Delegate listeners to chained source future. // If the source is complete, instead copy its values and // drop the chaining. _Future source = _chainSource; if (!source._isComplete) { source._addListener(listener); return; } _cloneResult(source); } D.assert(_isComplete); // Handle late listeners asynchronously. _zone.scheduleMicrotask(() => { _propagateToListeners(this, listener); return(null); }); } }
_FutureListener(_Future result, Delegate callback, Func <Exception, FutureOr> errorCallback, int state) { this.result = result; this.state = state; this.callback = callback; this.errorCallback = errorCallback; }
public static _FutureListener thenAwait( _Future result, _FutureOnValue onValue, Func <Exception, FutureOr> errorCallback) { return(new _FutureListener( result, onValue, errorCallback, ((errorCallback == null) ? stateThen : stateThenOnerror) | stateIsAwait )); }
public static _FutureListener catchError(_Future result, Func <Exception, FutureOr> errorCallback, _FutureErrorTest callback) { return(new _FutureListener( result, callback, errorCallback, (callback == null) ? stateCatcherror : stateCatcherrorTest )); }
internal static void _asyncCompleteWithErrorCallback(_Future result, Exception error) { AsyncError replacement = Zone.current.errorCallback(error); if (replacement != null) { error = _nonNullError(replacement.InnerException); } result._asyncCompleteError(error); }
public override Future timeout(TimeSpan timeLimit, Func <FutureOr> onTimeout = null) { if (_isComplete) { return(immediate(this)); } _Future result = new _Future(); Timer timer; if (onTimeout == null) { timer = Timer.create(timeLimit, () => { result._completeError( new TimeoutException("Future not completed", timeLimit)); return(null); }); } else { Zone zone = Zone.current; onTimeout = async_._registerHandler(onTimeout, zone); timer = Timer.create(timeLimit, () => { try { result._complete((FutureOr)zone.run(() => onTimeout())); } catch (Exception e) { result._completeError(e); } return(null); }); } then(v => { if (timer.isActive) { timer.cancel(); result._completeWithValue(v); } return(FutureOr.nil); }, onError: e => { if (timer.isActive) { timer.cancel(); result._completeError(e); } return(FutureOr.nil); }); return(result); }
public Future forEach(Action <T> action) { _Future future = new _Future(); StreamSubscription <T> subscription = null; subscription = listen( (T element) => { // TODO(floitsch): the type should be 'void' and inferred. _stream._runUserCode <object>(() => { action(element); return(default);
public override Future whenComplete(Func <FutureOr> action) { _Future result = new _Future(); if (!ReferenceEquals(result._zone, async_._rootZone)) { action = async_._registerHandler(action, result._zone); } _addListener(_FutureListener.whenComplete(result, action)); return(result); }
internal static void _cancelAndErrorWithReplacement <T>(StreamSubscription <T> subscription, _Future future, Exception error) { AsyncError replacement = Zone.current.errorCallback(error); if (replacement != null) { error = (Exception)_async._nonNullError(replacement); } _cancelAndError(subscription, future, error); }
static void _cancelAndError <T>(StreamSubscription <T> subscription, _Future future, Exception error ) { var cancelFuture = subscription.cancel(); if (cancelFuture != null && !Equals(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(() => future._completeError(error)); } else { future._completeError(error); } }
public static Future microtask(Func <FutureOr> computation) { _Future result = new _Future(); async_.scheduleMicrotask(() => { try { result._complete(computation()); } catch (Exception e) { async_._completeWithErrorCallback(result, e); } return(null); }); return(result); }
public override Future catchError(Func <Exception, FutureOr> onError, Func <Exception, bool> test = null) { _Future result = new _Future(); if (!ReferenceEquals(result._zone, async_._rootZone)) { onError = async_._registerErrorHandler(onError, result._zone); if (test != null) { test = async_._registerUnaryHandler(test, result._zone); } } _addListener(_FutureListener.catchError(result, onError, test)); return(result); }
Future <S> fold <S>(S initialValue, Func <S, T, S> combine) { _Future result = new _Future(); S value = initialValue; StreamSubscription <T> subscription = null; subscription = listen( (T element) => { _stream._runUserCode(() => combine(value, element), (S newValue) => { value = newValue; }, e => _stream._cancelAndErrorClosure(subscription, result)(e)); }, onError: (e, s) => result._completeError((Exception)e), onDone: () => { result._complete(FutureOr.value(value)); }, cancelOnError: true); return(result.to <S>()); }
public static Future create(Func <FutureOr> computation) { _Future result = new _Future(); Timer.run(() => { try { result._complete(computation()); } catch (Exception e) { async_._completeWithErrorCallback(result, e); } return(null); }); return(result); }
public static Future doWhile(Func <FutureOr> action) { _Future doneSignal = new _Future(); ZoneUnaryCallback nextIteration = null; // Bind this callback explicitly so that each iteration isn't bound in the // context of all the previous iterations' callbacks. // This avoids, e.g., deeply nested stack traces from the stack trace // package. nextIteration = Zone.current.bindUnaryCallbackGuarded((object keepGoingObj) => { bool keepGoing = (bool)keepGoingObj; while (keepGoing) { FutureOr result; try { result = action(); } catch (Exception error) { // Cannot use _completeWithErrorCallback because it completes // the future synchronously. async_._asyncCompleteWithErrorCallback(doneSignal, error); return(null); } if (result.isFuture) { result.f.then((value) => { nextIteration((bool)value); return(FutureOr.nil); }, onError: error => { doneSignal._completeError(error); return(FutureOr.nil); }); return(null); } keepGoing = (bool)result.v; } doneSignal._complete(); return(null); }); nextIteration(true); return(doneSignal); }
void _prependListeners(_FutureListener listeners) { if (listeners == null) { return; } if (_mayAddListener) { _FutureListener existingListeners = (_FutureListener)_resultOrListeners; _resultOrListeners = listeners; if (existingListeners != null) { _FutureListener cursor = listeners; while (cursor._nextListener != null) { cursor = cursor._nextListener; } cursor._nextListener = existingListeners; } } else { if (_isChained) { // Delegate listeners to chained source future. // If the source is complete, instead copy its values and // drop the chaining. _Future source = _chainSource; if (!source._isComplete) { source._prependListeners(listeners); return; } _cloneResult(source); } D.assert(_isComplete); listeners = _reverseListeners(listeners); _zone.scheduleMicrotask(() => { _propagateToListeners(this, listeners); return(null); }); } }
public override Future then(Func <object, FutureOr> f, Func <Exception, FutureOr> onError = null) { Zone currentZone = Zone.current; if (!ReferenceEquals(currentZone, async_._rootZone)) { f = async_._registerUnaryHandler(f, currentZone); if (onError != null) { onError = async_._registerErrorHandler(onError, currentZone); } } _Future result = new _Future(); _addListener(_FutureListener.then(result, f, onError)); return(result); }
Future <bool> contains(object needle) { _Future future = new _Future(); StreamSubscription <T> subscription = null; subscription = listen( (T element) => { _stream._runUserCode(() => (Equals(element, needle)), (bool isMatch) => { if (isMatch) { _stream._cancelAndValue(subscription, future, true); } }, (e) => _stream._cancelAndErrorClosure(subscription, future)(e)); }, onError: (e, _) => future._completeError((Exception)e), onDone: () => { future._complete(false); }, cancelOnError: true); return(future.to <bool>()); }
Future <T> reduce(Func <T, T, T> combine) { _Future result = new _Future(); bool seenFirst = false; T value = default; StreamSubscription <T> subscription = null; subscription = listen( (T element) => { if (seenFirst) { _stream._runUserCode(() => combine(value, element), (T newValue) => { value = newValue; }, onError: (e) => _stream._cancelAndErrorClosure(subscription, result)(e)); } else { value = element; seenFirst = true; } }, onError: (e, s) => result._completeError((Exception)e), onDone: () => { if (!seenFirst) { try { // Throw and recatch, instead of just doing // _completeWithErrorCallback, e, theError, StackTrace.current), // to ensure that the stackTrace is set on the error. throw new Exception("IterableElementError.noElement()"); } catch (Exception e) { async_._completeWithErrorCallback(result, e); } } else { // TODO: need check result._complete(FutureOr.value(value)); } }, cancelOnError: true); return(result.to <T>()); }
static void _chainCoreFuture(_Future source, _Future target) { D.assert(target._mayAddListener); // Not completed, not already chained. while (source._isChained) { source = source._chainSource; } if (source._isComplete) { _FutureListener listeners = target._removeListeners(); target._cloneResult(source); _propagateToListeners(target, listeners); } else { _FutureListener listeners = (_FutureListener)target._resultOrListeners; target._setChained(source); source._prependListeners(listeners); } }
public static Future delayed(TimeSpan duration, Func <FutureOr> computation = null) { _Future result = new _Future(); Timer.create(duration, () => { if (computation == null) { result._complete(); } else { try { result._complete(computation()); } catch (Exception e) { async_._completeWithErrorCallback(result, e); } } return(null); }); return(result); }
public static Future wait <T>(IEnumerable <Future> futures, bool eagerError = false, Action <T> cleanUp = null) { _Future result = new _Future(); List <T> values = null; // Collects the values. Set to null on error. int remaining = 0; // How many futures are we waiting for. Exception error = null; // The first error from a future. Func <Exception, FutureOr> handleError = (Exception theError) => { remaining--; if (values != null) { if (cleanUp != null) { foreach (var value in values) { if (value != null) { // Ensure errors from cleanUp are uncaught. sync(() => { cleanUp(value); return(FutureOr.nil); }); } } } values = null; if (remaining == 0 || eagerError) { result._completeError(theError); } else { error = theError; } } else if (remaining == 0 && !eagerError) { result._completeError(error); } return(FutureOr.nil); }; try { // As each future completes, put its value into the corresponding // position in the list of values. foreach (var future in futures) { int pos = remaining; future.then((object value) => { remaining--; if (values != null) { values[pos] = (T)value; if (remaining == 0) { result._completeWithValue(values); } } else { if (cleanUp != null && value != null) { // Ensure errors from cleanUp are uncaught. sync(() => { cleanUp((T)value); return(FutureOr.nil); }); } if (remaining == 0 && !eagerError) { result._completeError(error); } } return(FutureOr.nil); }, onError: handleError); // Increment the 'remaining' after the call to 'then'. // If that call throws, we don't expect any future callback from // the future, and we also don't increment remaining. remaining++; } if (remaining == 0) { return(value(FutureOr.value(new List <T>()))); } values = new List <T>(new T[remaining]); } catch (Exception e) { // The error must have been thrown while iterating over the futures // list, or while installing a callback handler on the future. if (remaining == 0 || eagerError) { // Throw a new Future.error. // Don't just call `result._completeError` since that would propagate // the error too eagerly, not giving the callers time to install // error handlers. // Also, don't use `_asyncCompleteError` since that one doesn't give // zones the chance to intercept the error. return(Future.error(e)); } else { // Don't allocate a list for values, thus indicating that there was an // error. // Set error to the caught exception. error = e; } } return(result); }