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 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); }