anchor.diff:              } else {
                 listenerValueOrError = new AsyncError(e, s);
               }
  -            listenerHasValue = false;
  +            listenerHasError = true;
               return;
             }
             if (completeResult is Future) {
  -            _Future result = listener.result;
  -            result._isChained = true;
  -            isPropagationAborted = true;
  -            completeResult.then((ignored) {
  -              _propagateToListeners(source, new _FutureListener.chain(result));
  -            }, onError: (error, [stackTrace]) {
  -              // When there is an error, we have to make the error the new
  -              // result of the current listener.
  -              if (completeResult is! _Future) {
  -                // This should be a rare case.
  -                completeResult = new _Future();
  -                completeResult._setError(error, stackTrace);
  +            if (completeResult is _Future && completeResult._isComplete) {
  +              if (completeResult._hasError) {
  +                listenerValueOrError = completeResult._error;
  +                listenerHasError = true;
                 }
  -              _propagateToListeners(completeResult,
  -                                    new _FutureListener.chain(result));
  -            });
  -          }
  -        }
  -
  -        if (!hasError) {
  +              // Otherwise use the existing result of source.
  +              return;
  +            }
  +            // We have to wait for the completeResult future to complete
  +            // before knowing if it's an error or we should use the result
  +            // of source.
  +            var originalSource = source;
  +            listenerValueOrError = completeResult.then((_) => originalSource);
  +            listenerHasError = false;
  +          }
  +        }
  +
  +        if (listener.handlesComplete) {
  +          // The whenComplete-handler is not combined with normal value/error
  +          // handling. This means at most one handleX method is called per
  +          // listener.
  +          assert(!listener.handlesValue);
  +          assert(!listener.handlesError);
  +          handleWhenCompleteCallback();
  +        } else if (!hasError) {
             if (listener.handlesValue) {
  -            listenerHasValue = handleValueCallback();
  +            handleValueCallback();
             }
           } else {
  -          handleError();
  -        }
  -        if (listener.handlesComplete) {
  -          handleWhenCompleteCallback();
  -        }
  +          if (listener.handlesError) {
  +            handleError();
  +          }
  +        }
  +
           // If we changed zone, oldZone will not be null.
           if (oldZone != null) Zone._leave(oldZone);
   
  -        if (isPropagationAborted) return;
           // If the listener's value is a future we need to chain it. Note that
  -        // this can only happen if there is a callback. Since 'is' checks
  -        // can be expensive, we're trying to avoid it.
  -        if (listenerHasValue &&
  -            !identical(sourceValue, listenerValueOrError) &&
  -            listenerValueOrError is Future) {
  +        // this can only happen if there is a callback.
  +        if (listenerValueOrError is Future) {
             Future chainSource = listenerValueOrError;
             // Shortcut if the chain-source is already completed. Just continue
             // the loop.
  
positive.code:     assert(source is _Future);
  
      // Mark the target as chained (and as such half-completed).
      target._isChained = true;
      _FutureListener listener = new _FutureListener.chain(target);
      if (source._isComplete) {
        _propagateToListeners(source, listener);
  
negative.code:   }
  
    // Take the value (when completed) of source and complete target with that
    // value (or error). This function could chain all Futures, but is slower
    // for _Future than _chainCoreFuture, so you must use _chainCoreFuture
    // in that case.
    static void _chainForeignFuture(Future source, _Future target) {
      assert(!target._isComplete);
      assert(source is! _Future);
  
      // Mark the target as chained (and as such half-completed).
      target._setChained();
      try {
        source.then((value) {
            assert(target._isChained);
  