Added a `ResultFuture` class.

This allows code to synchronously access the result of a wrapped future.

R=lrn@google.com

Review URL: https://codereview.chromium.org//1214843004 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dcd36cf..39d204b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@
 - Added `SubscriptionStream` which creates a single-subscription stream
   from an existing stream subscription.
 
+- Added a `ResultFuture` class for synchronously accessing the result of a
+  wrapped future.
+
 - Added `FutureGroup.onIdle` and `FutureGroup.isIdle`, which provide visibility
   into whether a group is actively waiting on any futures.
 
diff --git a/lib/async.dart b/lib/async.dart
index 97a574b..71a1849 100644
--- a/lib/async.dart
+++ b/lib/async.dart
@@ -13,6 +13,7 @@
 export "src/delegate/stream_sink.dart";
 export "src/delegate/stream_subscription.dart";
 export "src/future_group.dart";
+export "src/result_future.dart";
 export "src/stream_completer.dart";
 export "src/stream_group.dart";
 export "src/stream_queue.dart";
diff --git a/lib/src/result_future.dart b/lib/src/result_future.dart
new file mode 100644
index 0000000..311e83f
--- /dev/null
+++ b/lib/src/result_future.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library async.result_future;
+
+import 'dart:async';
+
+import '../result.dart';
+import 'delegate/future.dart';
+
+/// A [Future] wrapper that provides synchronous access to the result of the
+/// wrapped [Future] once it's completed.
+class ResultFuture<T> extends DelegatingFuture<T> {
+  /// Whether the future has fired and [result] is available.
+  bool get isComplete => result != null;
+
+  /// The result of the wrapped [Future], if it's completed.
+  ///
+  /// If it hasn't completed yet, this will be `null`.
+  Result<T> get result => _result;
+  Result<T> _result;
+
+  factory ResultFuture(Future<T> future) {
+    var resultFuture;
+    resultFuture = new ResultFuture._(Result.capture(future).then((result) {
+      resultFuture._result = result;
+      return result.asFuture;
+    }));
+    return resultFuture;
+  }
+
+  ResultFuture._(Future<T> future)
+      : super(future);
+}
diff --git a/test/result_future_test.dart b/test/result_future_test.dart
new file mode 100644
index 0000000..d8b4e01
--- /dev/null
+++ b/test/result_future_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+void main() {
+  var completer;
+  var future;
+  setUp(() {
+    completer = new Completer();
+    future = new ResultFuture(completer.future);
+  });
+
+  test('before completion, result is null', () {
+    expect(future.result, isNull);
+  });
+
+  test('after successful completion, result is the value of the future', () {
+    completer.complete(12);
+
+    // The completer calls its listeners asynchronously. We have to wait
+    // before we can access the result.
+    expect(future.then((_) => future.result.asValue.value),
+        completion(equals(12)));
+  });
+
+  test("after an error completion, result is the future's error", () {
+    var trace = new Trace.current();
+    completer.completeError('error', trace);
+
+    // The completer calls its listeners asynchronously. We have to wait
+    // before we can access the result.
+    return future.catchError((_) {}).then((_) {
+      var error = future.result.asError;
+      expect(error.error, equals('error'));
+      expect(error.stackTrace, equals(trace));
+    });
+  });
+}