예제 #1
0
        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);
        }
예제 #2
0
        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);
        }