Promises: Implement Promise<> handle with Then and Catch

Implements Then and Catch on top of AbstractPromise as well as
ManualPromiseResolver<> which is needed for testing at this
stage (PostTask doesn't yet return a promise). It's also
necessary for promises to interface with old style callback
based libraries which are extremly common in Chromium.

Design: https://docs.google.com/document/d/1l12PAJgEtlrqTXKiw6mk2cR2jP7FAfCCDr-DGIdiC9w/edit

Bug: 906125
Change-Id: Id7206698f37c59cfc201f3970cbf66a2ef89ee4e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1599620
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: Fran├žois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661045}
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5e65bd0..9436e7b2 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -755,9 +755,14 @@
     "task/promise/abstract_promise.h",
     "task/promise/dependent_list.cc",
     "task/promise/dependent_list.h",
+    "task/promise/helpers.h",
     "task/promise/no_op_promise_executor.cc",
     "task/promise/no_op_promise_executor.h",
+    "task/promise/promise.h",
+    "task/promise/promise_result.h",
     "task/promise/small_unique_object.h",
+    "task/promise/then_and_catch_executor.cc",
+    "task/promise/then_and_catch_executor.h",
     "task/scoped_set_task_priority_for_current_thread.cc",
     "task/scoped_set_task_priority_for_current_thread.h",
     "task/sequence_manager/associated_thread_id.cc",
@@ -2631,6 +2636,8 @@
     "task/post_task_unittest.cc",
     "task/promise/abstract_promise_unittest.cc",
     "task/promise/dependent_list_unittest.cc",
+    "task/promise/helpers_unittest.cc",
+    "task/promise/promise_unittest.cc",
     "task/scoped_set_task_priority_for_current_thread_unittest.cc",
     "task/sequence_manager/atomic_flag_set_unittest.cc",
     "task/sequence_manager/lazily_deallocated_deque_unittest.cc",
@@ -3034,6 +3041,7 @@
       "observer_list_unittest.nc",
       "optional_unittest.nc",
       "strings/string16_unittest.nc",
+      "task/promise/promise_unittest.nc",
       "task/task_traits_extension_unittest.nc",
       "task/task_traits_unittest.nc",
       "thread_annotations_unittest.nc",
diff --git a/base/callback_internal.h b/base/callback_internal.h
index 07c3dc4..0493dbf 100644
--- a/base/callback_internal.h
+++ b/base/callback_internal.h
@@ -19,14 +19,15 @@
 
 namespace internal {
 
-class CallbackBase;
-class CallbackBaseCopyable;
-
+class BaseThenAndCatchExecutor;
 class BindStateBase;
 
 template <typename Functor, typename... BoundArgs>
 struct BindState;
 
+class CallbackBase;
+class CallbackBaseCopyable;
+
 struct BindStateBaseRefCountTraits {
   static void Destruct(const BindStateBase*);
 };
@@ -135,6 +136,8 @@
   void Reset();
 
  protected:
+  friend class BaseThenAndCatchExecutor;
+
   using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
 
   // Returns true if this callback equals |other|. |other| may be null.
diff --git a/base/task/promise/abstract_promise.cc b/base/task/promise/abstract_promise.cc
index 0bd5f01..8b8a266 100644
--- a/base/task/promise/abstract_promise.cc
+++ b/base/task/promise/abstract_promise.cc
@@ -47,6 +47,11 @@
 void AbstractPromise::AddAsDependentForAllPrerequisites() {
   DCHECK(prerequisites_);
 
+  // Note a curried promise will eventually get to all its children and pass
+  // them catch responsibility through AddAsDependentForAllPrerequisites,
+  // although that'll be done lazily (only once they resolve/reject, so there
+  // is a possibility the DCHECKs might be racy.
+
   for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
     node.dependent_node.dependent = this;
 
@@ -196,20 +201,6 @@
   prerequisite->passed_catch_responsibility_ = true;
 }
 
-void AbstractPromise::PassCatchResponsibilityOntoDependentsForCurriedPromise(
-    DependentList::Node* dependent_list) {
-  CheckedAutoLock lock(GetCheckedLock());
-  if (!dependent_list)
-    return;
-
-  if (IsResolvedWithPromise()) {
-    for (DependentList::Node* node = dependent_list; node;
-         node = node->next.load(std::memory_order_relaxed)) {
-      node->dependent->MaybeInheritChecks(this);
-    }
-  }
-}
-
 AbstractPromise::LocationRef::LocationRef(const Location& from_here)
     : from_here_(from_here) {}
 
@@ -353,10 +344,6 @@
   DependentList::Node* dependent_list = dependents_.ConsumeOnceForResolve();
   dependent_list = NonThreadSafeReverseList(dependent_list);
 
-#if DCHECK_IS_ON()
-  PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
-#endif
-
   // Propagate resolve to dependents.
   DependentList::Node* next;
   for (DependentList::Node* node = dependent_list; node; node = next) {
@@ -374,10 +361,6 @@
   DependentList::Node* dependent_list = dependents_.ConsumeOnceForReject();
   dependent_list = NonThreadSafeReverseList(dependent_list);
 
-#if DCHECK_IS_ON()
-  PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
-#endif
-
   // Propagate rejection to dependents. We always propagate rejection
   // immediately.
   DependentList::Node* next;
@@ -429,7 +412,8 @@
 
 void AbstractPromise::OnResolved() {
 #if DCHECK_IS_ON()
-  DCHECK(executor_can_resolve_) << from_here_.ToString();
+  DCHECK(executor_can_resolve_ || IsResolvedWithPromise())
+      << from_here_.ToString();
 #endif
   if (IsResolvedWithPromise()) {
     scoped_refptr<AbstractPromise> curried_promise =
diff --git a/base/task/promise/abstract_promise.h b/base/task/promise/abstract_promise.h
index 33dde53..bac498a 100644
--- a/base/task/promise/abstract_promise.h
+++ b/base/task/promise/abstract_promise.h
@@ -46,7 +46,7 @@
 // resolved. This lets us disambiguate promises with the same resolve and reject
 // type.
 template <typename T>
-struct BASE_EXPORT Resolved {
+struct Resolved {
   using Type = T;
 
   static_assert(!std::is_same<T, NoReject>::value,
@@ -64,7 +64,7 @@
 };
 
 template <>
-struct BASE_EXPORT Resolved<void> {
+struct Resolved<void> {
   using Type = void;
   Void value;
 };
@@ -73,7 +73,7 @@
 // rejected. This lets us disambiguate promises with the same resolve and reject
 // type.
 template <typename T>
-struct BASE_EXPORT Rejected {
+struct Rejected {
   using Type = T;
   T value;
 
@@ -93,7 +93,7 @@
 };
 
 template <>
-struct BASE_EXPORT Rejected<void> {
+struct Rejected<void> {
   using Type = void;
   Void value;
 };
@@ -399,10 +399,6 @@
   void MaybeInheritChecks(AbstractPromise* source)
       EXCLUSIVE_LOCKS_REQUIRED(GetCheckedLock());
 
-  // Does nothing if this promise wasn't resolved by a promise.
-  void PassCatchResponsibilityOntoDependentsForCurriedPromise(
-      DependentList::Node* dependent_list);
-
   // Controls how we deal with unhandled rejection.
   const RejectPolicy reject_policy_;
 
diff --git a/base/task/promise/helpers.h b/base/task/promise/helpers.h
new file mode 100644
index 0000000..8a1104d
--- /dev/null
+++ b/base/task/promise/helpers.h
@@ -0,0 +1,519 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_PROMISE_HELPERS_H_
+#define BASE_TASK_PROMISE_HELPERS_H_
+
+#include <type_traits>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task/promise/abstract_promise.h"
+#include "base/task/promise/promise_result.h"
+
+namespace base {
+namespace internal {
+
+// PromiseCallbackTraits computes the resolve and reject types of a Promise
+// from the return type of a resolve or reject callback.
+//
+// Usage example:
+//   using Traits = PromiseCallbackTraits<int>;
+//
+//   Traits::
+//       ResolveType is int
+//       RejectType is NoReject
+//       could_resolve is true
+//       could_reject is false
+template <typename T>
+struct PromiseCallbackTraits {
+  using ResolveType = T;
+  using RejectType = NoReject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = false;
+};
+
+template <typename T>
+struct PromiseCallbackTraits<Resolved<T>> {
+  using ResolveType = T;
+  using RejectType = NoReject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = false;
+};
+
+template <typename T>
+struct PromiseCallbackTraits<Rejected<T>> {
+  using ResolveType = NoResolve;
+  using RejectType = T;
+  static constexpr bool could_resolve = false;
+  static constexpr bool could_reject = true;
+};
+
+template <typename Reject>
+struct PromiseCallbackTraits<Promise<NoResolve, Reject>> {
+  using ResolveType = NoResolve;
+  using RejectType = Reject;
+  static constexpr bool could_resolve = false;
+  static constexpr bool could_reject = true;
+};
+
+template <typename Resolve>
+struct PromiseCallbackTraits<Promise<Resolve, NoReject>> {
+  using ResolveType = Resolve;
+  using RejectType = NoReject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = false;
+};
+
+template <typename Resolve, typename Reject>
+struct PromiseCallbackTraits<Promise<Resolve, Reject>> {
+  using ResolveType = Resolve;
+  using RejectType = Reject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = true;
+};
+
+template <typename Reject>
+struct PromiseCallbackTraits<PromiseResult<NoResolve, Reject>> {
+  using ResolveType = NoResolve;
+  using RejectType = Reject;
+  static constexpr bool could_resolve = false;
+  static constexpr bool could_reject = true;
+};
+
+template <typename Resolve>
+struct PromiseCallbackTraits<PromiseResult<Resolve, NoReject>> {
+  using ResolveType = Resolve;
+  using RejectType = NoReject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = false;
+};
+
+template <typename Resolve, typename Reject>
+struct PromiseCallbackTraits<PromiseResult<Resolve, Reject>> {
+  using ResolveType = Resolve;
+  using RejectType = Reject;
+  static constexpr bool could_resolve = true;
+  static constexpr bool could_reject = true;
+};
+
+template <typename T>
+struct IsScopedRefPtr {
+  static constexpr bool value = false;
+};
+
+template <typename T>
+struct IsScopedRefPtr<scoped_refptr<T>> {
+  static constexpr bool value = true;
+};
+
+// UseMoveSemantics determines whether move semantics should be used to
+// pass |T| as a function parameter.
+//
+// Usage example:
+//
+//   UseMoveSemantics<std::unique_ptr<int>>::value;  // is true
+//   UseMoveSemantics<int>::value; // is false
+//   UseMoveSemantics<scoped_refptr<Dummy>>::value; // is false
+//
+// Will give false positives for some copyable types, but that should be
+// harmless.
+template <typename T,
+          bool use_move = !std::is_reference<T>::value &&
+                          !std::is_pointer<T>::value &&
+                          !std::is_fundamental<std::decay_t<T>>::value &&
+                          !IsScopedRefPtr<T>::value>
+struct UseMoveSemantics : public std::integral_constant<bool, use_move> {
+  static_assert(!std::is_rvalue_reference<T>::value,
+                "Promise<T&&> not supported");
+
+  static constexpr AbstractPromise::Executor::ArgumentPassingType
+      argument_passing_type =
+          use_move ? AbstractPromise::Executor::ArgumentPassingType::kMove
+                   : AbstractPromise::Executor::ArgumentPassingType::kNormal;
+};
+
+// CallbackTraits extracts properties relevant to Promises from a callback.
+//
+// Usage example:
+//
+//   using Traits = CallbackTraits<
+//       base::OnceCallback<PromiseResult<int, std::string>(float)>;
+//
+//   Traits::
+//     ResolveType is int
+//     RejectType is std::string
+//     ArgType is float
+//     ReturnType is PromiseResult<int, std::string>
+//     SignatureType is PromiseResult<int, std::string>(float)
+//     argument_passing_type is kNormal
+template <typename T>
+struct CallbackTraits;
+
+template <typename T>
+struct CallbackTraits<base::OnceCallback<T()>> {
+  using ResolveType = typename internal::PromiseCallbackTraits<T>::ResolveType;
+  using RejectType = typename internal::PromiseCallbackTraits<T>::RejectType;
+  using ArgType = void;
+  using ReturnType = T;
+  using SignatureType = T();
+  static constexpr AbstractPromise::Executor::ArgumentPassingType
+      argument_passing_type =
+          AbstractPromise::Executor::ArgumentPassingType::kNormal;
+};
+
+template <typename T>
+struct CallbackTraits<base::RepeatingCallback<T()>> {
+  using ResolveType = typename internal::PromiseCallbackTraits<T>::ResolveType;
+  using RejectType = typename internal::PromiseCallbackTraits<T>::RejectType;
+  using ArgType = void;
+  using ReturnType = T;
+  using SignatureType = T();
+  static constexpr AbstractPromise::Executor::ArgumentPassingType
+      argument_passing_type =
+          AbstractPromise::Executor::ArgumentPassingType::kNormal;
+};
+
+template <typename T, typename Arg>
+struct CallbackTraits<base::OnceCallback<T(Arg)>> {
+  using ResolveType = typename internal::PromiseCallbackTraits<T>::ResolveType;
+  using RejectType = typename internal::PromiseCallbackTraits<T>::RejectType;
+  using ArgType = Arg;
+  using ReturnType = T;
+  using SignatureType = T(Arg);
+  static constexpr AbstractPromise::Executor::ArgumentPassingType
+      argument_passing_type = UseMoveSemantics<Arg>::argument_passing_type;
+};
+
+template <typename T, typename Arg>
+struct CallbackTraits<base::RepeatingCallback<T(Arg)>> {
+  using ResolveType = typename internal::PromiseCallbackTraits<T>::ResolveType;
+  using RejectType = typename internal::PromiseCallbackTraits<T>::RejectType;
+  using ArgType = Arg;
+  using ReturnType = T;
+  using SignatureType = T(Arg);
+  static constexpr AbstractPromise::Executor::ArgumentPassingType
+      argument_passing_type = UseMoveSemantics<Arg>::argument_passing_type;
+};
+
+// Helper for combining the resolve types of two promises.
+template <typename A, typename B>
+struct ResolveCombinerHelper {
+  using Type = A;
+  static constexpr bool valid = false;
+};
+
+template <typename A>
+struct ResolveCombinerHelper<A, A> {
+  using Type = A;
+  static constexpr bool valid = true;
+};
+
+template <typename B>
+struct ResolveCombinerHelper<NoResolve, B> {
+  using Type = B;
+  static constexpr bool valid = true;
+};
+
+template <typename A>
+struct ResolveCombinerHelper<A, NoResolve> {
+  using Type = A;
+  static constexpr bool valid = true;
+};
+
+template <>
+struct ResolveCombinerHelper<NoResolve, NoResolve> {
+  using Type = NoResolve;
+  static constexpr bool valid = true;
+};
+
+// Helper for combining the reject types of two promises.
+template <typename A, typename B>
+struct RejectCombinerHelper {
+  using Type = A;
+  static constexpr bool valid = false;
+};
+
+template <typename A>
+struct RejectCombinerHelper<A, A> {
+  using Type = A;
+  static constexpr bool valid = true;
+};
+
+template <typename B>
+struct RejectCombinerHelper<NoReject, B> {
+  using Type = B;
+  static constexpr bool valid = true;
+};
+
+template <typename A>
+struct RejectCombinerHelper<A, NoReject> {
+  using Type = A;
+  static constexpr bool valid = true;
+};
+
+template <>
+struct RejectCombinerHelper<NoReject, NoReject> {
+  using Type = NoReject;
+  static constexpr bool valid = true;
+};
+
+// Helper that computes and validates the return type for combining promises.
+// Essentially the promise types have to match unless there's NoResolve or
+// or NoReject in which case they can be combined.
+template <typename ThenReturnResolveT,
+          typename ThenReturnRejectT,
+          typename CatchReturnResolveT,
+          typename CatchReturnRejectT>
+struct PromiseCombiner {
+  using ResolveHelper =
+      ResolveCombinerHelper<ThenReturnResolveT, CatchReturnResolveT>;
+  using RejectHelper =
+      RejectCombinerHelper<ThenReturnRejectT, CatchReturnRejectT>;
+  using ResolveType = typename ResolveHelper::Type;
+  using RejectType = typename RejectHelper::Type;
+  static constexpr bool valid = ResolveHelper::valid && RejectHelper::valid;
+};
+
+// TODO(alexclarke): Specialize |CallbackTraits| for callbacks with more than
+// one argument to support Promises::All.
+
+template <typename RejectStorage>
+struct EmplaceInnerHelper {
+  template <typename Resolve, typename Reject>
+  static void Emplace(AbstractPromise* promise,
+                      PromiseResult<Resolve, Reject> result) {
+    promise->emplace(std::move(result.value()));
+  }
+};
+
+// TODO(alexclarke): Specialize |EmplaceInnerHelper| where RejectStorage is
+// base::Variant to support Promises::All.
+
+template <typename ResolveStorage, typename RejectStorage>
+struct EmplaceHelper {
+  template <typename Resolve, typename Reject>
+  static void Emplace(AbstractPromise* promise,
+                      PromiseResult<Resolve, Reject>&& result) {
+    static_assert(std::is_same<typename ResolveStorage::Type, Resolve>::value ||
+                      std::is_same<NoResolve, Resolve>::value,
+                  "Resolve should match ResolveStorage");
+    static_assert(std::is_same<typename RejectStorage::Type, Reject>::value ||
+                      std::is_same<NoReject, Reject>::value,
+                  "Reject should match RejectStorage");
+    EmplaceInnerHelper<RejectStorage>::Emplace(promise, std::move(result));
+  }
+
+  template <typename Resolve, typename Reject>
+  static void Emplace(AbstractPromise* promise,
+                      Promise<Resolve, Reject>&& result) {
+    static_assert(std::is_same<typename ResolveStorage::Type, Resolve>::value ||
+                      std::is_same<NoResolve, Resolve>::value,
+                  "Resolve should match ResolveStorage");
+    static_assert(std::is_same<typename RejectStorage::Type, Reject>::value ||
+                      std::is_same<NoReject, Reject>::value,
+                  "Reject should match RejectStorage");
+    promise->emplace(std::move(result.abstract_promise_));
+  }
+
+  template <typename Result>
+  static void Emplace(AbstractPromise* promise, Result&& result) {
+    static_assert(std::is_same<typename ResolveStorage::Type, Result>::value,
+                  "Result should match ResolveStorage");
+    promise->emplace(Resolved<Result>{std::forward<Result>(result)});
+  }
+
+  template <typename Resolve>
+  static void Emplace(AbstractPromise* promise, Resolved<Resolve>&& resolved) {
+    static_assert(std::is_same<typename ResolveStorage::Type, Resolve>::value,
+                  "Resolve should match ResolveStorage");
+    promise->emplace(std::move(resolved));
+  }
+
+  template <typename Reject>
+  static void Emplace(AbstractPromise* promise, Rejected<Reject>&& rejected) {
+    static_assert(std::is_same<typename RejectStorage::Type, Reject>::value,
+                  "Reject should match RejectStorage");
+    promise->emplace(std::move(rejected));
+  }
+};
+
+// Helper that decides whether or not to std::move arguments for a callback
+// based on the type the callback specifies (i.e. we don't need to move if the
+// callback requests a reference).
+template <typename CbArg, typename ArgStorageType>
+class ArgMoveSemanticsHelper {
+ public:
+  static CbArg Get(AbstractPromise* arg) {
+    return GetImpl(arg, UseMoveSemantics<CbArg>());
+  }
+
+ private:
+  static CbArg GetImpl(AbstractPromise* arg, std::true_type should_move) {
+    return arg->TakeInnerValue<ArgStorageType>();
+  }
+
+  static CbArg GetImpl(AbstractPromise* arg, std::false_type should_move) {
+    return unique_any_cast<ArgStorageType>(&arg->value())->value;
+  }
+};
+
+// Helper for running a promise callback and storing the result if any.
+//
+// Callback = signature of the callback to execute,
+// ArgStorageType = type of the callback parameter (pr void if none)
+// ResolveStorage = type to use for resolve, usually Resolved<T>.
+// RejectStorage = type to use for reject, usually Rejected<T>.
+// TODO(alexclarke): Add support for Rejected<Variant<...>>.
+template <typename Callback,
+          typename ArgStorageType,
+          typename ResolveStorage,
+          typename RejectStorage>
+struct RunHelper;
+
+// Run helper for callbacks with a single argument.
+template <typename CbResult,
+          typename CbArg,
+          typename ArgStorageType,
+          typename ResolveStorage,
+          typename RejectStorage>
+struct RunHelper<OnceCallback<CbResult(CbArg)>,
+                 ArgStorageType,
+                 ResolveStorage,
+                 RejectStorage> {
+  using Callback = OnceCallback<CbResult(CbArg)>;
+
+  static void Run(Callback executor,
+                  AbstractPromise* arg,
+                  AbstractPromise* result) {
+    EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(
+        result, std::move(executor).Run(
+                    ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)));
+  }
+};
+
+// Run helper for callbacks with a single argument and void return value.
+template <typename CbArg,
+          typename ArgStorageType,
+          typename ResolveStorage,
+          typename RejectStorage>
+struct RunHelper<OnceCallback<void(CbArg)>,
+                 ArgStorageType,
+                 ResolveStorage,
+                 RejectStorage> {
+  using Callback = OnceCallback<void(CbArg)>;
+
+  static void Run(Callback executor,
+                  AbstractPromise* arg,
+                  AbstractPromise* result) {
+    static_assert(std::is_void<typename ResolveStorage::Type>::value, "");
+    std::move(executor).Run(
+        ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg));
+    result->emplace(Resolved<void>());
+  }
+};
+
+// Run helper for callbacks with no arguments.
+template <typename CbResult,
+          typename ArgStorageType,
+          typename ResolveStorage,
+          typename RejectStorage>
+struct RunHelper<OnceCallback<CbResult()>,
+                 ArgStorageType,
+                 ResolveStorage,
+                 RejectStorage> {
+  using Callback = OnceCallback<CbResult()>;
+
+  static void Run(Callback executor,
+                  AbstractPromise* arg,
+                  AbstractPromise* result) {
+    EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(
+        result, std::move(executor).Run());
+  }
+};
+
+// Run helper for callbacks with no arguments and void return type.
+template <typename ArgStorageType,
+          typename ResolveStorage,
+          typename RejectStorage>
+struct RunHelper<OnceCallback<void()>,
+                 ArgStorageType,
+                 ResolveStorage,
+                 RejectStorage> {
+  static void Run(OnceCallback<void()> executor,
+                  AbstractPromise* arg,
+                  AbstractPromise* result) {
+    static_assert(std::is_void<typename ResolveStorage::Type>::value, "");
+    std::move(executor).Run();
+    result->emplace(Resolved<void>());
+  }
+};
+
+// TODO(alexclarke): Specialize RunHelper for callbacks unpacked from a tuple.
+
+// Used by ManualPromiseResolver<> to generate callbacks.
+template <typename T, typename... Args>
+class PromiseCallbackHelper {
+ public:
+  using Callback = base::OnceCallback<void(Args&&...)>;
+  using RepeatingCallback = base::RepeatingCallback<void(Args&&...)>;
+
+  static Callback GetResolveCallback(scoped_refptr<AbstractPromise>& promise) {
+    return base::BindOnce(
+        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+          promise->emplace(Resolved<T>{std::forward<Args>(args)...});
+          promise->OnResolved();
+        },
+        promise);
+  }
+
+  static RepeatingCallback GetRepeatingResolveCallback(
+      scoped_refptr<AbstractPromise>& promise) {
+    return base::BindRepeating(
+        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+          promise->emplace(Resolved<T>{std::forward<Args>(args)...});
+          promise->OnResolved();
+        },
+        promise);
+  }
+
+  static Callback GetRejectCallback(scoped_refptr<AbstractPromise>& promise) {
+    return base::BindOnce(
+        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+          promise->emplace(Rejected<T>{std::forward<Args>(args)...});
+          promise->OnRejected();
+        },
+        promise);
+  }
+
+  static RepeatingCallback GetRepeatingRejectCallback(
+      scoped_refptr<AbstractPromise>& promise) {
+    return base::BindRepeating(
+        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+          promise->emplace(Rejected<T>{std::forward<Args>(args)...});
+          promise->OnRejected();
+        },
+        promise);
+  }
+};
+
+// Validates that the argument type |CallbackArgType| of a resolve or
+// reject callback is compatible with the resolve or reject type
+// |PromiseType| of this Promise.
+template <typename PromiseType, typename CallbackArgType>
+struct IsValidPromiseArg {
+  static constexpr bool value =
+      std::is_same<PromiseType, std::decay_t<CallbackArgType>>::value;
+};
+
+template <typename PromiseType, typename CallbackArgType>
+struct IsValidPromiseArg<PromiseType&, CallbackArgType> {
+  static constexpr bool value =
+      std::is_same<PromiseType&, CallbackArgType>::value;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_HELPERS_H_
diff --git a/base/task/promise/helpers_unittest.cc b/base/task/promise/helpers_unittest.cc
new file mode 100644
index 0000000..edaecce
--- /dev/null
+++ b/base/task/promise/helpers_unittest.cc
@@ -0,0 +1,284 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/promise/helpers.h"
+
+#include "base/bind.h"
+#include "base/task/promise/promise.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/do_nothing_promise.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+TEST(UseMoveSemantics, GeneralTypes) {
+  static_assert(!UseMoveSemantics<int>::value,
+                "Integral types don't need to be moved");
+
+  static_assert(UseMoveSemantics<std::unique_ptr<int>>::value,
+                "Move only types should be moved");
+
+  static_assert(
+      !UseMoveSemantics<scoped_refptr<AbstractPromise>>::value,
+      "To support multiple callbacks scoped_refptr doesn't need to be moved.");
+}
+
+TEST(CallbackTraits, CallbackReferenceTypes) {
+  static_assert(
+      std::is_same<int&,
+                   CallbackTraits<Callback<int&(int&)>>::ResolveType>::value,
+      "");
+
+  static_assert(
+      std::is_same<int&, CallbackTraits<Callback<int&(int&)>>::ArgType>::value,
+      "");
+}
+
+TEST(CallbackTraits, RepeatingCallbackReferenceTypes) {
+  static_assert(
+      std::is_same<
+          int&,
+          CallbackTraits<RepeatingCallback<int&(int&)>>::ResolveType>::value,
+      "");
+
+  static_assert(
+      std::is_same<
+          int&, CallbackTraits<RepeatingCallback<int&(int&)>>::ArgType>::value,
+      "");
+}
+
+TEST(PromiseCombiner, InvalidCombos) {
+  static_assert(!PromiseCombiner<Resolved<int>, Rejected<float>, Resolved<int>,
+                                 Rejected<bool>>::valid,
+                "Invalid, reject types differ");
+  static_assert(!PromiseCombiner<Resolved<int>, Rejected<float>, Resolved<void>,
+                                 Rejected<float>>::valid,
+                "Invalid, resolve types differ");
+}
+
+TEST(PromiseCombiner, TypesMatch) {
+  using Result = PromiseCombiner<Resolved<int>, Rejected<float>, Resolved<int>,
+                                 Rejected<float>>;
+  static_assert(Result::valid, "Types are the same, should match");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, NoResolve) {
+  using Result = PromiseCombiner<NoResolve, Rejected<float>, Resolved<int>,
+                                 Rejected<float>>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, NoResolve2) {
+  using Result = PromiseCombiner<Resolved<int>, Rejected<float>, NoResolve,
+                                 Rejected<float>>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, BothNoResolve) {
+  using Result =
+      PromiseCombiner<NoResolve, Rejected<float>, NoResolve, Rejected<float>>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, NoResolve>::value,
+                "Resolve type should be NoResolve");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, NoReject) {
+  using Result =
+      PromiseCombiner<Resolved<int>, NoReject, Resolved<int>, Rejected<float>>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, NoReject2) {
+  using Result =
+      PromiseCombiner<Resolved<int>, Rejected<float>, Resolved<int>, NoReject>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, BothNoReject) {
+  using Result =
+      PromiseCombiner<Resolved<int>, NoReject, Resolved<int>, NoReject>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, NoReject>::value,
+                "Resolve type should be NoReject");
+}
+
+TEST(PromiseCombiner, NoResolveAndNoReject) {
+  using Result =
+      PromiseCombiner<Resolved<int>, NoReject, NoResolve, Rejected<float>>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(PromiseCombiner, NoResolveAndNoReject2) {
+  using Result =
+      PromiseCombiner<NoResolve, Rejected<float>, Resolved<int>, NoReject>;
+  static_assert(Result::valid, "Valid combination");
+
+  static_assert(std::is_same<Result::ResolveType, Resolved<int>>::value,
+                "Resolve type should be int");
+
+  static_assert(std::is_same<Result::RejectType, Rejected<float>>::value,
+                "Resolve type should be float");
+}
+
+TEST(EmplaceHelper, EmplacePromiseResult) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> reject = DoNothingPromiseBuilder(FROM_HERE);
+
+  EmplaceHelper<Resolved<int>, Rejected<std::string>>::Emplace(
+      resolve.get(), PromiseResult<int, std::string>(123));
+  EmplaceHelper<Resolved<int>, Rejected<std::string>>::Emplace(
+      reject.get(), PromiseResult<int, std::string>("Oh no!"));
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(resolve->value()).value, 123);
+  EXPECT_EQ(unique_any_cast<Rejected<std::string>>(reject->value()).value,
+            "Oh no!");
+}
+
+TEST(EmplaceHelper, EmplacePromise) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> curried = DoNothingPromiseBuilder(FROM_HERE);
+
+  EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace(
+      resolve.get(), Promise<int>(std::move(curried)));
+
+  EXPECT_TRUE(resolve->IsResolvedWithPromise());
+}
+
+TEST(EmplaceHelper, NakedType) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+
+  EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace(resolve.get(), 123);
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(resolve->value()).value, 123);
+}
+
+TEST(EmplaceHelper, ReferenceType) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+
+  int a = 12345;
+
+  EmplaceHelper<Resolved<int&>, Rejected<NoReject>>::Emplace<int&>(
+      resolve.get(), a);
+
+  EXPECT_EQ(unique_any_cast<Resolved<int&>>(resolve->value()).value, 12345);
+}
+
+TEST(EmplaceHelper, ResolvedInt) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+
+  EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace(resolve.get(),
+                                                            Resolved<int>(123));
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(resolve->value()).value, 123);
+}
+
+TEST(EmplaceHelper, RejectedString) {
+  scoped_refptr<AbstractPromise> resolve = DoNothingPromiseBuilder(FROM_HERE);
+
+  EmplaceHelper<Resolved<void>, Rejected<std::string>>::Emplace(
+      resolve.get(), Rejected<std::string>("Whoops!"));
+
+  EXPECT_EQ(unique_any_cast<Rejected<std::string>>(resolve->value()).value,
+            "Whoops!");
+}
+
+TEST(RunHelper, CallbackVoidArgumentIntResult) {
+  scoped_refptr<AbstractPromise> arg = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE);
+
+  RunHelper<OnceCallback<int()>, Resolved<void>, Resolved<int>,
+            Rejected<std::string>>::Run(BindOnce([]() { return 123; }),
+                                        arg.get(), result.get());
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(result->value()).value, 123);
+}
+
+TEST(RunHelper, CallbackVoidArgumentVoidResult) {
+  scoped_refptr<AbstractPromise> arg = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE);
+
+  RunHelper<OnceCallback<void()>, Resolved<void>, Resolved<void>,
+            Rejected<std::string>>::Run(BindOnce([]() {}), arg.get(),
+                                        result.get());
+
+  EXPECT_EQ(result->value().type(), TypeId::From<Resolved<void>>());
+}
+
+TEST(RunHelper, CallbackIntArgumentIntResult) {
+  scoped_refptr<AbstractPromise> arg = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE);
+  arg->emplace(Resolved<int>(123));
+
+  RunHelper<OnceCallback<int(int)>, Resolved<int>, Resolved<int>,
+            Rejected<std::string>>::Run(BindOnce([](int value) {
+                                          return value + 1;
+                                        }),
+                                        arg.get(), result.get());
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(result->value()).value, 124);
+}
+
+TEST(RunHelper, CallbackIntArgumentArgumentVoidResult) {
+  scoped_refptr<AbstractPromise> arg = DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<AbstractPromise> result = DoNothingPromiseBuilder(FROM_HERE);
+  arg->emplace(Resolved<int>(123));
+
+  int value;
+  RunHelper<OnceCallback<void(int)>, Resolved<int>, Resolved<void>,
+            Rejected<std::string>>::Run(BindLambdaForTesting([&](int arg) {
+                                          value = arg;
+                                        }),
+                                        arg.get(), result.get());
+
+  EXPECT_EQ(value, 123);
+  EXPECT_EQ(result->value().type(), TypeId::From<Resolved<void>>());
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/promise.h b/base/task/promise/promise.h
new file mode 100644
index 0000000..cb70a2f6
--- /dev/null
+++ b/base/task/promise/promise.h
@@ -0,0 +1,425 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_PROMISE_PROMISE_H_
+#define BASE_TASK_PROMISE_PROMISE_H_
+
+#include "base/task/post_task.h"
+#include "base/task/promise/helpers.h"
+#include "base/task/promise/no_op_promise_executor.h"
+#include "base/task/promise/promise_result.h"
+#include "base/task/promise/then_and_catch_executor.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+
+// Inspired by ES6 promises, Promise<> is a PostTask based callback system for
+// asynchronous operations. An operation can resolve (succeed) with a value and
+// optionally reject (fail) with a different result. Interested parties can be
+// notified using ThenOn() and CatchOn() which schedule callbacks to run as
+// appropriate on the specified task runner or task traits. If a promise is
+// settled when a ThenOn() / CatchOn() / FinallyOn() statement is added, the
+// callback will be posted immediately, otherwise it has to wait.
+//
+// Promise<> is copyable, moveable and thread safe. Under the hood
+// internal::AbstractPromise is refcounted so retaining multiple Promises<> will
+// prevent that part of the promise graph from being released.
+template <typename ResolveType, typename RejectType = NoReject>
+class Promise {
+ public:
+  Promise() : abstract_promise_(nullptr) {}
+
+  explicit Promise(
+      scoped_refptr<internal::AbstractPromise> abstract_promise) noexcept
+      : abstract_promise_(std::move(abstract_promise)) {}
+
+  // Constructs an unresolved promise for use by a ManualPromiseResolver<> and
+  // TaskRunner::PostPromise.
+  Promise(scoped_refptr<TaskRunner> task_runner,
+          const Location& location,
+          RejectPolicy reject_policy)
+      : abstract_promise_(MakeRefCounted<internal::AbstractPromise>(
+            std::move(task_runner),
+            location,
+            nullptr,
+            reject_policy,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructUnresolved,
+                internal::NoOpPromiseExecutor>(),
+            /* can_resolve */ !std::is_same<ResolveType, NoResolve>::value,
+            /* can_reject */ !std::is_same<RejectType, NoReject>::value)) {}
+
+  NOINLINE ~Promise() = default;
+
+  operator bool() const { return !!abstract_promise_; }
+
+  bool IsCancelledForTesting() const {
+    DCHECK(abstract_promise_);
+    return abstract_promise_->IsCanceled();
+  }
+
+  // A task to execute |on_reject| is posted on |task_runner| as soon as this
+  // promise (or an uncaught ancestor) is rejected. A Promise<> for the return
+  // value of |on_reject| is returned.
+  //
+  // The following callback return types have special meanings:
+  // 1. PromiseResult<Resolve, Reject> lets the callback resolve, reject or
+  //    curry a Promise<Resolve, Reject>
+  // 2. Promise<Resolve, Reject> where the result is a curried promise.
+  //
+  // If a promise has multiple Catches they will be run in order of creation.
+  template <typename RejectCb>
+  NOINLINE auto CatchOn(scoped_refptr<TaskRunner> task_runner,
+                        const Location& from_here,
+                        RejectCb&& on_reject) noexcept {
+    DCHECK(abstract_promise_);
+
+    // Extract properties from the |on_reject| callback.
+    using RejectCallbackTraits = internal::CallbackTraits<RejectCb>;
+    using RejectCallbackArgT = typename RejectCallbackTraits::ArgType;
+
+    // Compute the resolve and reject types of the returned Promise.
+    using ReturnedPromiseTraits =
+        internal::PromiseCombiner<ResolveType,
+                                  NoReject,  // We've caught the reject case.
+                                  typename RejectCallbackTraits::ResolveType,
+                                  typename RejectCallbackTraits::RejectType>;
+    using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+    using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+    static_assert(!std::is_same<NoReject, RejectType>::value,
+                  "Can't catch a NoReject promise.");
+
+    // Check we wouldn't need to return Promise<Variant<...>, ...>
+    static_assert(ReturnedPromiseTraits::valid,
+                  "Ambiguous promise resolve type");
+    static_assert(
+        internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value ||
+            std::is_void<RejectCallbackArgT>::value,
+        "|on_reject| callback must accept Promise::RejectType or void.");
+
+    return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+        MakeRefCounted<internal::AbstractPromise>(
+            std::move(task_runner), from_here,
+            std::make_unique<internal::AbstractPromise::AdjacencyList>(
+                abstract_promise_),
+            RejectPolicy::kMustCatchRejection,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructUnresolved,
+                internal::ThenAndCatchExecutor<
+                    OnceClosure,  // Never called.
+                    OnceCallback<typename RejectCallbackTraits::SignatureType>,
+                    internal::NoCallback, RejectType,
+                    Resolved<ReturnedPromiseResolveT>,
+                    Rejected<ReturnedPromiseRejectT>>>(),
+            OnceClosure(),
+            static_cast<
+                OnceCallback<typename RejectCallbackTraits::SignatureType>>(
+                std::forward<RejectCb>(on_reject))));
+  }
+
+  template <typename RejectCb>
+  auto CatchOn(const TaskTraits& traits,
+               const Location& from_here,
+               RejectCb&& on_reject) noexcept {
+    return CatchOn(CreateTaskRunnerWithTraits(traits), from_here,
+                   std::forward<RejectCb>(on_reject));
+  }
+
+  template <typename RejectCb>
+  auto CatchOnCurrent(const Location& from_here,
+                      RejectCb&& on_reject) noexcept {
+    return CatchOn(SequencedTaskRunnerHandle::Get(), from_here,
+                   std::forward<RejectCb>(on_reject));
+  }
+
+  // A task to execute |on_resolve| is posted on |task_runner| as soon as this
+  // promise (or an unhandled ancestor) is resolved. A Promise<> for the return
+  // value of |on_resolve| is returned.
+  //
+  // The following callback return types have special meanings:
+  // 1. PromiseResult<Resolve, Reject> lets the callback resolve, reject or
+  //    curry a Promise<Resolve, Reject>
+  // 2. Promise<Resolve, Reject> where the result is a curried promise.
+  //
+  // If a promise has multiple Thens they will be run in order of creation.
+  template <typename ResolveCb>
+  NOINLINE auto ThenOn(scoped_refptr<TaskRunner> task_runner,
+                       const Location& from_here,
+                       ResolveCb&& on_resolve) noexcept {
+    DCHECK(abstract_promise_);
+
+    // Extract properties from the |on_resolve| callback.
+    using ResolveCallbackTraits =
+        internal::CallbackTraits<std::decay_t<ResolveCb>>;
+    using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType;
+
+    // Compute the resolve and reject types of the returned Promise.
+    using ReturnedPromiseTraits =
+        internal::PromiseCombiner<NoResolve,  // We've caught the resolve case.
+                                  RejectType,
+                                  typename ResolveCallbackTraits::ResolveType,
+                                  typename ResolveCallbackTraits::RejectType>;
+    using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+    using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+    // Check we wouldn't need to return Promise<..., Variant<...>>
+    static_assert(ReturnedPromiseTraits::valid,
+                  "Ambiguous promise reject type");
+
+    static_assert(
+        internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value ||
+            std::is_void<ResolveCallbackArgT>::value,
+        "|on_resolve| callback must accept Promise::ResolveType or void.");
+
+    return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+        MakeRefCounted<internal::AbstractPromise>(
+            std::move(task_runner), from_here,
+            std::make_unique<internal::AbstractPromise::AdjacencyList>(
+                abstract_promise_),
+            RejectPolicy::kMustCatchRejection,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructUnresolved,
+                internal::ThenAndCatchExecutor<
+                    OnceCallback<typename ResolveCallbackTraits::SignatureType>,
+                    OnceClosure, ResolveType, internal::NoCallback,
+                    Resolved<ReturnedPromiseResolveT>,
+                    Rejected<ReturnedPromiseRejectT>>>(),
+            std::forward<ResolveCb>(on_resolve), OnceClosure()));
+  }
+
+  template <typename ResolveCb>
+  auto ThenOn(const TaskTraits& traits,
+              const Location& from_here,
+              ResolveCb&& on_resolve) noexcept {
+    return ThenOn(CreateTaskRunnerWithTraits(traits), from_here,
+                  std::forward<ResolveCb>(on_resolve));
+  }
+
+  template <typename ResolveCb>
+  auto ThenOnCurrent(const Location& from_here,
+                     ResolveCb&& on_resolve) noexcept {
+    return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
+                  std::forward<ResolveCb>(on_resolve));
+  }
+
+  // A task to execute |on_reject| is posted on |task_runner| as soon as this
+  // promise (or an uncaught ancestor) is rejected. Likewise a task to execute
+  // |on_resolve| is posted on |task_runner| as soon as this promise (or an
+  // unhandled ancestor) is resolved. A Promise<> for the return value of
+  // |on_resolve| or |on_reject| is returned.
+  //
+  // The following callback return types have special meanings:
+  // 1. PromiseResult<Resolve, Reject> lets the callback resolve, reject or
+  //    curry a Promise<Resolve, Reject>
+  // 2. Promise<Resolve, Reject> where the result is a curried promise.
+  //
+  // If a promise has multiple Catches/ Thens, they will be run in order of
+  // creation.
+  //
+  // Note if either |on_resolve| or |on_reject| are canceled (due to weak
+  // pointer invalidation), then the other must be canceled at the same time as
+  // well. This restriction only applies to this form of ThenOn/ThenOnCurrent.
+  template <typename ResolveCb, typename RejectCb>
+  NOINLINE auto ThenOn(scoped_refptr<TaskRunner> task_runner,
+                       const Location& from_here,
+                       ResolveCb&& on_resolve,
+                       RejectCb&& on_reject) noexcept {
+    DCHECK(abstract_promise_);
+
+    // Extract properties from the |on_resolve| and |on_reject| callbacks.
+    using ResolveCallbackTraits = internal::CallbackTraits<ResolveCb>;
+    using RejectCallbackTraits = internal::CallbackTraits<RejectCb>;
+    using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType;
+    using RejectCallbackArgT = typename RejectCallbackTraits::ArgType;
+
+    // Compute the resolve and reject types of the returned Promise.
+    using ReturnedPromiseTraits =
+        internal::PromiseCombiner<typename ResolveCallbackTraits::ResolveType,
+                                  typename ResolveCallbackTraits::RejectType,
+                                  typename RejectCallbackTraits::ResolveType,
+                                  typename RejectCallbackTraits::RejectType>;
+    using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+    using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+    static_assert(!std::is_same<NoReject, RejectType>::value,
+                  "Can't catch a NoReject promise.");
+
+    static_assert(ReturnedPromiseTraits::valid,
+                  "|on_resolve| callback and |on_resolve| callback must return "
+                  "compatible types.");
+
+    static_assert(
+        internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value ||
+            std::is_void<ResolveCallbackArgT>::value,
+        "|on_resolve| callback must accept Promise::ResolveType or void.");
+
+    static_assert(
+        internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value ||
+            std::is_void<RejectCallbackArgT>::value,
+        "|on_reject| callback must accept Promise::RejectType or void.");
+
+    return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+        MakeRefCounted<internal::AbstractPromise>(
+            std::move(task_runner), from_here,
+            std::make_unique<internal::AbstractPromise::AdjacencyList>(
+                abstract_promise_),
+            RejectPolicy::kMustCatchRejection,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructUnresolved,
+                internal::ThenAndCatchExecutor<
+                    OnceCallback<typename ResolveCallbackTraits::SignatureType>,
+                    OnceCallback<typename RejectCallbackTraits::SignatureType>,
+                    ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>,
+                    Rejected<ReturnedPromiseRejectT>>>(),
+            static_cast<
+                OnceCallback<typename ResolveCallbackTraits::SignatureType>>(
+                std::forward<ResolveCb>(on_resolve)),
+            static_cast<
+                OnceCallback<typename RejectCallbackTraits::SignatureType>>(
+                std::forward<RejectCb>(on_reject))));
+  }
+
+  template <typename ResolveCb, typename RejectCb>
+  auto ThenOn(const TaskTraits& traits,
+              const Location& from_here,
+              ResolveCb&& on_resolve,
+              RejectCb&& on_reject) noexcept {
+    return ThenOn(CreateTaskRunnerWithTraits(traits), from_here,
+                  std::forward<ResolveCb>(on_resolve),
+                  std::forward<RejectCb>(on_reject));
+  }
+
+  template <typename ResolveCb, typename RejectCb>
+  auto ThenOnCurrent(const Location& from_here,
+                     ResolveCb&& on_resolve,
+                     RejectCb&& on_reject) noexcept {
+    return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
+                  std::forward<ResolveCb>(on_resolve),
+                  std::forward<RejectCb>(on_reject));
+  }
+
+  template <typename... Args>
+  NOINLINE static Promise<ResolveType, RejectType> CreateResolved(
+      const Location& from_here,
+      Args&&... args) noexcept {
+    scoped_refptr<internal::AbstractPromise> promise(
+        MakeRefCounted<internal::AbstractPromise>(
+            nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructResolved,
+                internal::NoOpPromiseExecutor>(),
+            /* can_resolve */ true,
+            /* can_reject */ false));
+    promise->emplace(Resolved<ResolveType>{std::forward<Args>(args)...});
+    return Promise<ResolveType, RejectType>(std::move(promise));
+  }
+
+  template <typename... Args>
+  NOINLINE static Promise<ResolveType, RejectType> CreateRejected(
+      const Location& from_here,
+      Args&&... args) noexcept {
+    scoped_refptr<internal::AbstractPromise> promise(
+        MakeRefCounted<internal::AbstractPromise>(
+            nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection,
+            internal::AbstractPromise::ConstructWith<
+                internal::DependentList::ConstructRejected,
+                internal::NoOpPromiseExecutor>(),
+            /* can_resolve */ false,
+            /* can_reject */ true));
+    promise->emplace(Rejected<RejectType>{std::forward<Args>(args)...});
+    return Promise<ResolveType, RejectType>(std::move(promise));
+  }
+
+  using ResolveT = ResolveType;
+  using RejectT = RejectType;
+
+ private:
+  template <typename A, typename B>
+  friend class Promise;
+
+  template <typename A, typename B>
+  friend class PromiseResult;
+
+  template <typename RejectStorage, typename ResultStorage>
+  friend struct internal::EmplaceHelper;
+
+  template <typename A, typename B>
+  friend class ManualPromiseResolver;
+
+  scoped_refptr<internal::AbstractPromise> abstract_promise_;
+};
+
+// Used for manually resolving and rejecting a Promise. This is for
+// compatibility with old code and will eventually be removed.
+template <typename ResolveType, typename RejectType = NoReject>
+class ManualPromiseResolver {
+ public:
+  using ResolveHelper = std::conditional_t<
+      std::is_void<ResolveType>::value,
+      internal::PromiseCallbackHelper<void>,
+      internal::PromiseCallbackHelper<ResolveType, ResolveType>>;
+
+  using RejectHelper = std::conditional_t<
+      std::is_void<RejectType>::value,
+      internal::PromiseCallbackHelper<void>,
+      internal::PromiseCallbackHelper<RejectType, RejectType>>;
+
+  ManualPromiseResolver(
+      const Location& from_here,
+      RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection)
+      : promise_(SequencedTaskRunnerHandle::Get(), from_here, reject_policy) {}
+
+  template <typename... Args>
+  void Resolve(Args&&... arg) noexcept {
+    DCHECK(!promise_.abstract_promise_->IsResolved());
+    DCHECK(!promise_.abstract_promise_->IsRejected());
+    static_assert(!std::is_same<NoResolve, ResolveType>::value,
+                  "Can't resolve a NoResolve promise.");
+    promise_.abstract_promise_->emplace(
+        Resolved<ResolveType>{std::forward<Args>(arg)...});
+    promise_.abstract_promise_->OnResolved();
+  }
+
+  template <typename... Args>
+  void Reject(Args&&... arg) noexcept {
+    DCHECK(!promise_.abstract_promise_->IsResolved());
+    DCHECK(!promise_.abstract_promise_->IsRejected());
+    static_assert(!std::is_same<NoReject, RejectType>::value,
+                  "Can't reject a NoReject promise.");
+    promise_.abstract_promise_->emplace(
+        Rejected<RejectType>{std::forward<Args>(arg)...});
+    promise_.abstract_promise_->OnRejected();
+  }
+
+  typename ResolveHelper::Callback GetResolveCallback() {
+    return ResolveHelper::GetResolveCallback(promise_.abstract_promise_);
+  }
+
+  typename ResolveHelper::RepeatingCallback GetRepeatingResolveCallback() {
+    return ResolveHelper::GetRepeatingResolveCallback(
+        promise_.abstract_promise_);
+  }
+
+  typename RejectHelper::Callback GetRejectCallback() {
+    static_assert(!std::is_same<NoReject, RejectType>::value,
+                  "Can't reject a NoReject promise.");
+    return RejectHelper::GetRejectCallback(promise_.abstract_promise_);
+  }
+
+  typename RejectHelper::RepeatingCallback GetRepeatingRejectCallback() {
+    static_assert(!std::is_same<NoReject, RejectType>::value,
+                  "Can't reject a NoReject promise.");
+    return RejectHelper::GetRepeatingRejectCallback(promise_.abstract_promise_);
+  }
+
+  Promise<ResolveType, RejectType>& promise() { return promise_; }
+
+ private:
+  Promise<ResolveType, RejectType> promise_;
+};
+
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_PROMISE_H_
diff --git a/base/task/promise/promise_result.h b/base/task/promise/promise_result.h
new file mode 100644
index 0000000..50949c9
--- /dev/null
+++ b/base/task/promise/promise_result.h
@@ -0,0 +1,145 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_PROMISE_PROMISE_RESULT_H_
+#define BASE_TASK_PROMISE_PROMISE_RESULT_H_
+
+#include <type_traits>
+
+#include "base/containers/unique_any.h"
+
+namespace base {
+
+// An optional return type from a promise callback, which allows the callback to
+// decide whether or not it should reject.  E.g.
+//
+// enum class Error { kReason };
+//
+// PromiseResult<int, Error> MyFn() {
+//   int result;
+//   ...
+//   if (error)
+//     return Error::kReason;
+//   return result;
+// }
+//
+// If ResolveType and RejectType are distinct PromiseResult's constructor will
+// accept them.  E.g.
+//
+// PromiseResult<int, std::string> pr(123);       // Resolve
+// PromiseResult<int, std::string> pr("whoops");  // Reject
+//
+// If ResolveType and RejectType are the same you need to use Resolved<> and
+// Rejected<> to disambiguate.  E.g.
+//
+// PromiseResult<int, int> pr(Resolved{123});     // Resolve
+// PromiseResult<int, int> pr(Rejected{123});     // Reject
+//
+// PromiseResult<ResolveType, RejectType> has a constructor that accepts
+// Promise<ResolveType, RejectType>.
+template <typename ResolveType, typename RejectType = NoReject>
+class PromiseResult {
+ public:
+  template <typename ResolveT = ResolveType,
+            typename RejectT = RejectType,
+            class Enable = std::enable_if<std::is_void<ResolveT>::value ^
+                                          std::is_void<RejectT>::value>>
+  PromiseResult() : PromiseResult(typename Analyze<void>::TagType()) {}
+
+  template <typename T>
+  PromiseResult(T&& t)
+      : PromiseResult(typename Analyze<std::decay_t<T>>::TagType(),
+                      std::forward<T>(t)) {}
+
+  PromiseResult(PromiseResult&& other) noexcept = default;
+
+  PromiseResult(const PromiseResult&) = delete;
+  PromiseResult& operator=(const PromiseResult&) = delete;
+
+  unique_any& value() { return value_; }
+
+ private:
+  struct IsWrapped {};
+  struct IsResolved {};
+  struct IsRejected {};
+  struct IsPromise {};
+
+  // Helper that assigns one of the above tags for |T|.
+  template <typename T>
+  struct Analyze {
+    using DecayedT = std::decay_t<T>;
+
+    static constexpr bool is_resolve =
+        std::is_convertible<DecayedT, std::decay_t<ResolveType>>::value;
+
+    static constexpr bool is_reject =
+        std::is_convertible<DecayedT, std::decay_t<RejectType>>::value;
+
+    static_assert(!is_reject || !std::is_same<DecayedT, NoReject>::value,
+                  "A NoReject promise can't reject");
+
+    static_assert(!std::is_same<std::decay_t<ResolveType>,
+                                std::decay_t<RejectType>>::value,
+                  "Ambiguous because ResolveType and RejectType are the same");
+
+    static_assert(is_resolve || is_reject,
+                  "Argument matches neither resolve nor reject type.");
+
+    static_assert(
+        is_resolve != is_reject && (is_resolve || is_reject),
+        "Ambiguous because argument matches both ResolveType and RejectType");
+
+    using TagType = std::conditional_t<is_resolve, IsResolved, IsRejected>;
+  };
+
+  template <typename T>
+  struct Analyze<Resolved<T>> {
+    using TagType = IsWrapped;
+    static_assert(std::is_same<ResolveType, T>::value,
+                  "T in Resolved<T> is not ResolveType");
+  };
+
+  template <typename T>
+  struct Analyze<Rejected<T>> {
+    static_assert(std::is_same<RejectType, T>::value,
+                  "T in Rejected<T> is not RejectType");
+
+    static_assert(!std::is_same<RejectType, NoReject>::value,
+                  "A NoReject promise can't reject");
+    using TagType = IsWrapped;
+  };
+
+  template <typename ResolveT, typename RejectT>
+  struct Analyze<Promise<ResolveT, RejectT>> {
+    using TagType = IsPromise;
+
+    static_assert(std::is_same<ResolveT, ResolveType>::value,
+                  "Promise resolve types don't match");
+
+    static_assert(std::is_same<RejectT, RejectType>::value,
+                  "Promise reject types don't match");
+  };
+
+  template <typename... Args>
+  PromiseResult(IsResolved, Args&&... args)
+      : value_(in_place_type_t<Resolved<ResolveType>>(),
+               std::forward<Args>(args)...) {}
+
+  template <typename... Args>
+  PromiseResult(IsRejected, Args&&... args)
+      : value_(in_place_type_t<Rejected<RejectType>>(),
+               std::forward<Args>(args)...) {}
+
+  PromiseResult(IsPromise, const Promise<ResolveType, RejectType>& promise)
+      : value_(promise.abstract_promise_) {}
+
+  template <typename T>
+  PromiseResult(IsWrapped, T&& t) : value_(std::forward<T>(t)) {}
+
+  unique_any value_;
+};
+
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_PROMISE_RESULT_H_
diff --git a/base/task/promise/promise_unittest.cc b/base/task/promise/promise_unittest.cc
new file mode 100644
index 0000000..aed4bb7
--- /dev/null
+++ b/base/task/promise/promise_unittest.cc
@@ -0,0 +1,1405 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/task/promise/promise.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/do_nothing_promise.h"
+#include "base/test/gtest_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace base {
+namespace {
+
+void RecordOrder(std::vector<int>* run_order, int order) {
+  run_order->push_back(order);
+}
+
+class ObjectToDelete : public RefCounted<ObjectToDelete> {
+ public:
+  // |delete_flag| is set to true when this object is deleted
+  ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) {
+    EXPECT_FALSE(*delete_flag_);
+  }
+
+ private:
+  friend class RefCounted<ObjectToDelete>;
+  ~ObjectToDelete() { *delete_flag_ = true; }
+
+  bool* const delete_flag_;
+
+  DISALLOW_COPY_AND_ASSIGN(ObjectToDelete);
+};
+
+class MockObject {
+ public:
+  MockObject() = default;
+
+  void Task(scoped_refptr<ObjectToDelete>) {}
+  void Reply(scoped_refptr<ObjectToDelete>) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockObject);
+};
+
+struct DummyError {};
+
+}  // namespace
+
+class PromiseTest : public testing::Test {
+ public:
+  test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) {
+  scoped_refptr<TestMockTimeTaskRunner> post_runner =
+      MakeRefCounted<TestMockTimeTaskRunner>();
+  scoped_refptr<TestMockTimeTaskRunner> reply_runner =
+      MakeRefCounted<TestMockTimeTaskRunner>(
+          TestMockTimeTaskRunner::Type::kBoundToThread);
+  MockObject mock_object;
+  bool delete_task_flag = false;
+  bool delete_reply_flag = false;
+
+  Promise<int>::CreateResolved(FROM_HERE, 42)
+      .ThenOn(post_runner, FROM_HERE,
+              BindOnce(&MockObject::Task, Unretained(&mock_object),
+                       MakeRefCounted<ObjectToDelete>(&delete_task_flag)))
+      .ThenOnCurrent(
+          FROM_HERE,
+          BindOnce(&MockObject::Reply, Unretained(&mock_object),
+                   MakeRefCounted<ObjectToDelete>(&delete_reply_flag)));
+
+  post_runner->ClearPendingTasks();
+
+  post_runner = nullptr;
+  reply_runner = nullptr;
+
+  EXPECT_TRUE(delete_task_flag);
+  EXPECT_TRUE(delete_reply_flag);
+}
+
+TEST_F(PromiseTest, GetResolveCallbackThen) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+  p.GetResolveCallback().Run(123);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                              EXPECT_EQ(123, result);
+                              run_loop.Quit();
+                            }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, GetRejectCallbackCatch) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(
+      FROM_HERE, BindLambdaForTesting([&](int result) {
+        run_loop.Quit();
+        FAIL() << "We shouldn't get here, the promise was rejected!";
+      }),
+      BindLambdaForTesting([&](const std::string& err) {
+        run_loop.Quit();
+        EXPECT_EQ("Oh no!", err);
+      }));
+
+  p.GetRejectCallback().Run(std::string("Oh no!"));
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, GetRepeatingResolveCallbackThen) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+  p.GetRepeatingResolveCallback().Run(123);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                              EXPECT_EQ(123, result);
+                              run_loop.Quit();
+                            }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, GetRepeatingRejectCallbackCatch) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(
+      FROM_HERE, BindLambdaForTesting([&](int result) {
+        run_loop.Quit();
+        FAIL() << "We shouldn't get here, the promise was rejected!";
+      }),
+      BindLambdaForTesting([&](const std::string& err) {
+        run_loop.Quit();
+        EXPECT_EQ("Oh no!", err);
+      }));
+
+  p.GetRepeatingRejectCallback().Run(std::string("Oh no!"));
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CreateResolvedThen) {
+  RunLoop run_loop;
+  Promise<int>::CreateResolved(FROM_HERE, 123)
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(123, result);
+                       run_loop.Quit();
+                     }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CatchOnCurrentReturnTypes) {
+  ManualPromiseResolver<int, void> p1(FROM_HERE);
+
+  // Check CatchOnCurrent returns the expected return types for various
+  // return types.
+  Promise<int> r1 =
+      p1.promise().CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r2 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
+  Promise<int, int> r3 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
+
+  Promise<int, void> r4 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); }));
+  Promise<int> r5 = p1.promise().CatchOnCurrent(
+      FROM_HERE,
+      BindOnce([]() { return PromiseResult<int, NoReject>(123.0); }));
+  Promise<int, int> r6 = p1.promise().CatchOnCurrent(
+      FROM_HERE,
+      BindOnce([]() { return PromiseResult<NoResolve, int>(123.0); }));
+
+  Promise<int, void> r7 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<int, void>(); }));
+  Promise<int> r8 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); }));
+  Promise<int, int> r9 = p1.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<NoResolve, int>(); }));
+
+  ManualPromiseResolver<NoResolve, void> p2(FROM_HERE);
+  Promise<int> r10 =
+      p2.promise().CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r11 = p2.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
+  Promise<NoResolve, int> r12 = p2.promise().CatchOnCurrent(
+      FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
+}
+
+TEST_F(PromiseTest, ThenOnCurrentReturnTypes) {
+  ManualPromiseResolver<std::string, void> p1(FROM_HERE);
+
+  // Check ThenOnCurrent returns the expected return types for various
+  // return types.
+  Promise<int, void> r1 =
+      p1.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int, void> r2 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
+  Promise<NoResolve, void> r3 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Rejected<void>(); }));
+
+  Promise<int, void> r4 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); }));
+  Promise<int, void> r5 = p1.promise().ThenOnCurrent(
+      FROM_HERE,
+      BindOnce([]() { return PromiseResult<int, NoReject>(123.0); }));
+  Promise<NoResolve, void> r6 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return PromiseResult<NoResolve, void>(); }));
+
+  Promise<int, void> r7 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<int, void>(); }));
+  Promise<int, void> r8 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); }));
+  Promise<NoResolve, void> r9 = p1.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Promise<NoResolve, void>(); }));
+
+  ManualPromiseResolver<std::string> p2(FROM_HERE);
+  Promise<int> r10 =
+      p2.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r11 = p2.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
+  Promise<NoResolve, int> r12 = p2.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
+}
+
+TEST_F(PromiseTest, ThenAndCatchOnCurrentReturnTypes) {
+  struct A {};
+  struct B {};
+  struct C {};
+  struct D {};
+
+  Promise<B, NoReject> p1 =
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
+  Promise<NoResolve, B> p2 =
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
+  Promise<B, C> p3 =
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
+
+  Promise<B, NoReject> p4 =
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
+  Promise<NoResolve, B> p5 =
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
+  Promise<B, C> p6 =
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
+
+  Promise<B, C> p7 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
+  Promise<NoResolve, C> p8 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<C>(); }));
+  Promise<B, C> p9 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
+
+  Promise<A, NoReject> p10 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<A>(); }));
+  Promise<A, B> p11 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
+  Promise<A, B> p12 =
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<A, B> { return B{}; }));
+
+  Promise<C, NoReject> p13 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
+          BindOnce([]() { return Resolved<C>(); }));
+  Promise<NoResolve, D> p14 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
+          BindOnce([]() { return Rejected<D>(); }));
+  Promise<C, D> p15 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
+          BindOnce([]() { return Rejected<D>(); }));
+  Promise<C, D> p16 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
+          BindOnce([]() { return Resolved<C>(); }));
+
+  Promise<C, D> p17 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+          BindOnce([]() { return Resolved<C>(); }));
+  Promise<C, D> p18 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+          BindOnce([]() { return Rejected<D>(); }));
+  Promise<C, D> p19 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
+          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+  Promise<C, D> p20 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
+          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+
+  Promise<C, D> p21 =
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+}
+
+TEST_F(PromiseTest, RejectAndReReject) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .CatchOnCurrent(
+          FROM_HERE,
+          BindOnce([](const std::string& err) -> PromiseResult<int, int> {
+            EXPECT_EQ("Oh no!", err);
+            // Re-Reject with -1 this time.
+            return Rejected<int>(-1);
+          }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
+                        EXPECT_EQ(-1, err);
+                        run_loop.Quit();
+                        return -1;
+                      }));
+
+  p.GetRejectCallback().Run("Oh no!");
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectAndReRejectThenCatch) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([](std::string) {
+                        return Rejected<int>(-1);
+                      }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                     [&](int) { return Resolved<int>(1000); }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int value) {
+                       EXPECT_EQ(1000, value);
+                       return Rejected<DummyError>();
+                     }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                     [&](DummyError) { run_loop.Quit(); }));
+
+  p.GetRejectCallback().Run("Oh no!");
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenWhichAlwayResolves) {
+  ManualPromiseResolver<void> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Resolved<int> {
+                       // Resolve
+                       return 123;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int value) {
+                       EXPECT_EQ(123, value);
+                       run_loop.Quit();
+                     }));
+
+  p.GetResolveCallback().Run();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenWhichAlwayRejects) {
+  ManualPromiseResolver<void, int> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Rejected<int> {
+                       // Reject
+                       return -1;
+                     }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
+                        EXPECT_EQ(-1, err);
+                        run_loop.Quit();
+                      }));
+
+  p.GetResolveCallback().Run();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenWhichAlwayRejectsTypeTwo) {
+  ManualPromiseResolver<void> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Rejected<int> {
+                       // Reject
+                       return -1;
+                     }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
+                        EXPECT_EQ(-1, err);
+                        run_loop.Quit();
+                      }));
+
+  p.GetResolveCallback().Run();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenWhichAlwayRejectsTypeThree) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+
+  base::RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       return Rejected<std::string>(std::string("reject"));
+                     }))
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::string result) {
+                        run_loop.Quit();
+                      }));
+
+  p.GetResolveCallback().Run(123);
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ReferenceType) {
+  int a = 123;
+  int b = 456;
+  ManualPromiseResolver<int&> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int& value) -> int& {
+                       EXPECT_EQ(123, value);
+                       return b;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int& value) {
+                       EXPECT_EQ(456, value);
+                       run_loop.Quit();
+                     }));
+
+  p.GetResolveCallback().Run(a);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, PromiseResultVoid) {
+  ManualPromiseResolver<void> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                    [&]() { return PromiseResult<void>(); }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&]() { run_loop.Quit(); }));
+
+  p.Resolve();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RefcountedType) {
+  scoped_refptr<internal::AbstractPromise> a =
+      DoNothingPromiseBuilder(FROM_HERE);
+  scoped_refptr<internal::AbstractPromise> b =
+      DoNothingPromiseBuilder(FROM_HERE);
+  ManualPromiseResolver<scoped_refptr<internal::AbstractPromise>> p(FROM_HERE);
+  RunLoop run_loop;
+
+  p.promise()
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting(
+                         [&](scoped_refptr<internal::AbstractPromise> value) {
+                           EXPECT_EQ(a, value);
+                           return b;
+                         }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting(
+                         [&](scoped_refptr<internal::AbstractPromise> value) {
+                           EXPECT_EQ(b, value);
+                           run_loop.Quit();
+                         }));
+
+  p.Resolve(a);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ResolveThenVoidFunction) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+  p.Resolve(123);
+
+  // You don't have to use the resolve (or reject) arguments from the
+  // previous promise.
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(FROM_HERE,
+                            BindLambdaForTesting([&]() { run_loop.Quit(); }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ResolveAfterThen) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                              EXPECT_EQ(123, result);
+                              run_loop.Quit();
+                            }));
+
+  p.Resolve(123);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectOutsidePromiseAfterThen) {
+  ManualPromiseResolver<int, void> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(
+      FROM_HERE, BindLambdaForTesting([&](int result) {
+        run_loop.Quit();
+        FAIL() << "We shouldn't get here, the promise was rejected!";
+      }),
+      run_loop.QuitClosure());
+
+  p.Reject();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenChainMoveOnlyType) {
+  ManualPromiseResolver<std::unique_ptr<int>> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&](std::unique_ptr<int> result) {
+                       EXPECT_THAT(123, *result);
+                       run_loop.Quit();
+                     }));
+
+  p.Resolve(std::make_unique<int>(123));
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, MultipleMovesNotAllowed) {
+  ManualPromiseResolver<std::unique_ptr<int>> p(FROM_HERE);
+
+  // The executor argument will be called with move semantics.
+  p.promise().ThenOnCurrent(FROM_HERE,
+                            BindOnce([](std::unique_ptr<int> result) {}));
+
+  // It's an error to do that twice.
+  EXPECT_DCHECK_DEATH({
+    p.promise().ThenOnCurrent(FROM_HERE,
+                              BindOnce([](std::unique_ptr<int> result) {}));
+  });
+}
+
+TEST_F(PromiseTest, ThenChain) {
+  ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                       result.push_back(1);
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                       result.push_back(2);
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                       result.push_back(3);
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&](std::vector<size_t> result) {
+                       EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
+                       run_loop.Quit();
+                     }));
+
+  p.Resolve(std::vector<size_t>{0});
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectionInThenChainDefaultVoid) {
+  ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                       result.push_back(result.size());
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                       result.push_back(result.size());
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindOnce([](std::vector<size_t> result)
+                                  -> PromiseResult<std::vector<size_t>, void> {
+                       return Rejected<void>();
+                     }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](std::vector<size_t> result) {
+            FAIL() << "We shouldn't get here, the promise was rejected!";
+          }),
+          BindLambdaForTesting([&]() { run_loop.Quit(); }));
+
+  p.Resolve(std::vector<size_t>{0});
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectPropagation) {
+  ManualPromiseResolver<int, bool> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenOnCurrent(
+          FROM_HERE,
+          BindOnce([](int result) -> PromiseResult<int, std::string> {
+            return std::string("Fail shouldn't get here");
+          }),
+          BindOnce([](bool value) -> PromiseResult<int, std::string> {
+            EXPECT_FALSE(value);
+            return std::string("Oh no!");
+          }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](int result) {
+            FAIL() << "We shouldn't get here, the promise was rejected!";
+            run_loop.Quit();
+          }),
+          BindLambdaForTesting([&](const std::string& err) {
+            EXPECT_EQ("Oh no!", err);
+            run_loop.Quit();
+          }));
+
+  p.Reject(false);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectPropagationThensAfterRejectSkipped) {
+  ManualPromiseResolver<int, bool> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenOnCurrent(
+          FROM_HERE,
+          BindOnce([](int result) -> PromiseResult<int, std::string> {
+            return std::string("Fail shouldn't get here");
+          }),
+          BindOnce([](bool value) -> PromiseResult<int, std::string> {
+            EXPECT_FALSE(value);
+            return std::string("Oh no!");  // Reject
+          }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       CHECK(false) << "Shouldn't get here";
+                       return result + 1;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       CHECK(false) << "Shouldn't get here";
+                       return result + 1;
+                     }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](int result) {
+            FAIL() << "We shouldn't get here, the promise was rejected!";
+            run_loop.Quit();
+          }),
+          BindLambdaForTesting([&](const std::string& err) {
+            EXPECT_EQ("Oh no!", err);
+            run_loop.Quit();
+          }));
+
+  p.Reject(false);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes) {
+  ManualPromiseResolver<void, int> p(FROM_HERE);
+
+  // Make sure ThenOnCurrent returns the expected type.
+  Promise<int, std::string> p2 = p.promise().ThenOnCurrent(
+      FROM_HERE,
+      BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
+      BindOnce([](int err) -> Resolved<int> { return 123; }));
+}
+
+TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes2) {
+  ManualPromiseResolver<void, int> p(FROM_HERE);
+
+  // Make sure ThenOnCurrent returns the expected type.
+  Promise<int, std::string> p2 = p.promise().ThenOnCurrent(
+      FROM_HERE,
+      BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
+      BindOnce([](int err) -> Rejected<std::string> { return "123"; }));
+}
+
+TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes3) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+
+  // Make sure ThenOnCurrent returns the expected type.
+  Promise<void, bool> p2 = p.promise().ThenOnCurrent(
+      FROM_HERE, BindOnce([](int value) -> PromiseResult<void, bool> {
+        if (value % 2) {
+          return Resolved<void>();
+        } else {
+          return true;
+        }
+      }),
+      BindOnce([](const std::string& err) -> Rejected<bool> { return false; }));
+}
+
+TEST_F(PromiseTest, ThenOnAfterNoResolvePromiseResult) {
+  ManualPromiseResolver<std::unique_ptr<int>, int> p1(FROM_HERE);
+
+  RunLoop run_loop;
+  p1.promise()
+      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                     [&](int) -> PromiseResult<NoResolve, int> {
+                                       return Rejected<int>();
+                                     }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int>) {
+                       run_loop.Quit();
+                       return std::make_unique<int>(42);
+                     }),
+                     BindLambdaForTesting([&](int err) {
+                       CHECK(false) << "Shouldn't get here";
+                       return std::make_unique<int>(42);
+                     }));
+
+  p1.GetResolveCallback().Run(std::make_unique<int>(42));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CatchCreatesNoRejectPromise) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+
+  // Make sure CatchOnCurrent returns the expected type.
+  Promise<int> p2 =
+      p.promise()
+          .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int) {
+                           return Rejected<std::string>();
+                         }))
+          .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::string) {
+                            return Resolved<int>();
+                          }));
+}
+
+TEST_F(PromiseTest, ResolveSkipsCatches) {
+  ManualPromiseResolver<int, void> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                        CHECK(false) << "Shouldn't get here";
+                        return -1;
+                      }))
+      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                        CHECK(false) << "Shouldn't get here";
+                        return -1;
+                      }))
+      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                        CHECK(false) << "Shouldn't get here";
+                        return -1;
+                      }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](int result) {
+            EXPECT_EQ(2, result);
+            run_loop.Quit();
+          }),
+          BindLambdaForTesting([&]() {
+            FAIL() << "We shouldn't get here, the promise was resolved!";
+            run_loop.Quit();
+          }));
+
+  p.Resolve(1);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ThenChainVariousReturnTypes) {
+  ManualPromiseResolver<void> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([]() { return 5; }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       EXPECT_EQ(5, result);
+                       return std::string("Hello");
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](std::string result) {
+                       EXPECT_EQ("Hello", result);
+                       return true;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](bool result) {
+                       EXPECT_TRUE(result);
+                       run_loop.Quit();
+                     }));
+
+  p.GetResolveCallback().Run();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CurriedVoidPromise) {
+  Promise<void> p = Promise<void>::CreateResolved(FROM_HERE);
+  ManualPromiseResolver<void> promise_resolver(FROM_HERE);
+
+  RunLoop run_loop;
+  p.ThenOnCurrent(FROM_HERE,
+                  BindOnce(
+                      [](ManualPromiseResolver<void>* promise_resolver) {
+                        return promise_resolver->promise();
+                      },
+                      &promise_resolver))
+      .ThenOnCurrent(FROM_HERE, run_loop.QuitClosure());
+  RunLoop().RunUntilIdle();
+
+  promise_resolver.Resolve();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CurriedIntPromise) {
+  Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000);
+  ManualPromiseResolver<int> promise_resolver(FROM_HERE);
+
+  RunLoop run_loop;
+  p.ThenOnCurrent(
+       FROM_HERE,
+       BindOnce(
+           [](ManualPromiseResolver<int>* promise_resolver, int result) {
+             EXPECT_EQ(1000, result);
+             return promise_resolver->promise();
+           },
+           &promise_resolver))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(123, result);
+                       run_loop.Quit();
+                     }));
+  RunLoop().RunUntilIdle();
+
+  promise_resolver.Resolve(123);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, PromiseResultReturningAPromise) {
+  Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000);
+  ManualPromiseResolver<int> promise_resolver(FROM_HERE);
+
+  RunLoop run_loop;
+  p.ThenOnCurrent(FROM_HERE,
+                  BindLambdaForTesting([&](int result) -> PromiseResult<int> {
+                    EXPECT_EQ(1000, result);
+                    return promise_resolver.promise();
+                  }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(123, result);
+                       run_loop.Quit();
+                     }));
+  RunLoop().RunUntilIdle();
+
+  promise_resolver.Resolve(123);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, ResolveToDisambiguateThenReturnValue) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE,
+                     BindOnce([](int i) -> PromiseResult<Value, Value> {
+                       if ((i % 2) == 1)
+                         return Resolved<Value>("Success it was odd.");
+                       return Rejected<Value>("Failure it was even.");
+                     }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](Value result) {
+            EXPECT_EQ("Success it was odd.", result.GetString());
+            run_loop.Quit();
+          }),
+          BindLambdaForTesting([&](Value err) {
+            run_loop.Quit();
+            FAIL() << "We shouldn't get here, the promise was resolved!";
+          }));
+
+  p.Resolve(1);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, RejectedToDisambiguateThenReturnValue) {
+  ManualPromiseResolver<int, int> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, int> {
+                       return Rejected<int>(123);
+                     }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&](int result) {
+            run_loop.Quit();
+            FAIL() << "We shouldn't get here, the promise was rejected!";
+          }),
+          BindLambdaForTesting([&](int err) {
+            run_loop.Quit();
+            EXPECT_EQ(123, err);
+          }));
+
+  p.Resolve();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, NestedPromises) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+  p.Resolve(100);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       ManualPromiseResolver<int> p2(FROM_HERE);
+                       p2.Resolve(200);
+                       return p2.promise().ThenOnCurrent(
+                           FROM_HERE, BindOnce([](int result) {
+                             ManualPromiseResolver<int> p3(FROM_HERE);
+                             p3.Resolve(300);
+                             return p3.promise().ThenOnCurrent(
+                                 FROM_HERE,
+                                 BindOnce([](int result) { return result; }));
+                           }));
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(300, result);
+                       run_loop.Quit();
+                     }));
+
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, Catch) {
+  ManualPromiseResolver<int, std::string> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
+      .CatchOnCurrent(FROM_HERE,
+                      BindLambdaForTesting([&](const std::string& err) {
+                        EXPECT_EQ("Whoops!", err);
+                        run_loop.Quit();
+                        return -1;
+                      }));
+
+  p.Reject("Whoops!");
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, BranchedThenChainExecutionOrder) {
+  std::vector<int> run_order;
+
+  ManualPromiseResolver<void> promise_a(FROM_HERE);
+  Promise<void> promise_b =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1));
+
+  Promise<void> promise_c =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3));
+
+  Promise<void> promise_d =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
+
+  promise_a.Resolve();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, ElementsAre(0, 2, 4, 1, 3, 5));
+}
+
+TEST_F(PromiseTest, BranchedThenChainWithCatchExecutionOrder) {
+  std::vector<int> run_order;
+
+  ManualPromiseResolver<void, void> promise_a(FROM_HERE);
+  Promise<void> promise_b =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1))
+          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2));
+
+  Promise<void> promise_c =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
+          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
+
+  Promise<void> promise_d =
+      promise_a.promise()
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 6))
+          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 7))
+          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 8));
+
+  promise_a.Reject();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, ElementsAre(2, 5, 8));
+}
+
+TEST_F(PromiseTest, CatchRejectInThenChain) {
+  ManualPromiseResolver<int> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOnCurrent(
+          FROM_HERE,
+          BindOnce([](int result) -> PromiseResult<int, std::string> {
+            return std::string("Whoops!");
+          }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       CHECK(false) << "Shouldn't get here";
+                       return result;
+                     }))
+      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
+                       CHECK(false) << "Shouldn't get here";
+                       return result;
+                     }))
+      .CatchOnCurrent(FROM_HERE,
+                      BindLambdaForTesting([&](const std::string& err) {
+                        EXPECT_EQ("Whoops!", err);
+                        run_loop.Quit();
+                        return -1;
+                      }));
+
+  p.Resolve(123);
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CatchThenVoid) {
+  ManualPromiseResolver<int, void> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(123, result);
+                       run_loop.Quit();
+                     }));
+
+  p.Reject();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, CatchThenInt) {
+  ManualPromiseResolver<int, int> p(FROM_HERE);
+
+  RunLoop run_loop;
+  p.promise()
+      .CatchOnCurrent(FROM_HERE, BindOnce([](int err) { return err + 1; }))
+      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
+                       EXPECT_EQ(124, result);
+                       run_loop.Quit();
+                     }));
+
+  p.Reject(123);
+  run_loop.Run();
+}
+
+namespace {
+struct Cancelable {
+  Cancelable() : weak_ptr_factory(this) {}
+
+  void LogTask(std::vector<std::string>* log, std::string value) {
+    log->push_back(value);
+  }
+
+  void NopTask() {}
+
+  WeakPtrFactory<Cancelable> weak_ptr_factory;
+};
+}  // namespace
+
+TEST_F(PromiseTest, CancelViaWeakPtr) {
+  std::vector<std::string> log;
+  ManualPromiseResolver<void, std::string> mpr(FROM_HERE,
+                                               RejectPolicy::kCatchNotRequired);
+  Promise<void, std::string> p1 = mpr.promise();
+  {
+    Cancelable cancelable;
+    Promise<void, std::string> p2 = p1.ThenOnCurrent(
+        FROM_HERE,
+        BindOnce(&Cancelable::LogTask, cancelable.weak_ptr_factory.GetWeakPtr(),
+                 &log, "Then #1"));
+    p2.ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                    [&]() -> PromiseResult<void, std::string> {
+                                      log.push_back("Then #2 (reject)");
+                                      return std::string("Whoops!");
+                                    }))
+        .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                      [&]() { log.push_back("Then #3"); }))
+        .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
+                                      [&]() { log.push_back("Then #4"); }))
+        .CatchOnCurrent(FROM_HERE,
+                        BindLambdaForTesting([&](const std::string& err) {
+                          log.push_back("Caught " + err);
+                        }));
+
+    p2.ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&]() { log.push_back("Then #5"); }));
+    p2.ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&]() { log.push_back("Then #6"); }));
+  }
+
+  mpr.Resolve();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(log.empty());
+}
+
+TEST_F(PromiseTest, CatchNotRequired) {
+  ManualPromiseResolver<bool, int> p(FROM_HERE,
+                                     RejectPolicy::kCatchNotRequired);
+
+  RunLoop run_loop;
+  p.promise().ThenOnCurrent(FROM_HERE, run_loop.QuitClosure());
+
+  // Note this doesn't DCHECK even though we haven't specified a Catch.
+  p.Resolve();
+  run_loop.Run();
+}
+
+TEST_F(PromiseTest, MoveOnlyTypeMultipleThensNotAllowed) {
+#if DCHECK_IS_ON()
+  Promise<std::unique_ptr<int>> p =
+      Promise<std::unique_ptr<int>>::CreateResolved(FROM_HERE,
+                                                    std::make_unique<int>(123));
+
+  p.ThenOnCurrent(FROM_HERE,
+                  BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
+
+  EXPECT_DCHECK_DEATH({
+    p.ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> i) {
+                      EXPECT_EQ(123, *i);
+                    }));
+  });
+#endif
+}
+
+TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) {
+#if DCHECK_IS_ON()
+  auto p = Promise<void, std::unique_ptr<int>>::CreateRejected(
+      FROM_HERE, std::make_unique<int>(123));
+
+  p.CatchOnCurrent(
+      FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
+
+  EXPECT_DCHECK_DEATH({
+    p.CatchOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> i) {
+                       EXPECT_EQ(123, *i);
+                     }));
+  });
+#endif
+}
+
+TEST_F(PromiseTest, PostTaskUnhandledRejection) {
+#if DCHECK_IS_ON()
+  Promise<void, int> p =
+      Promise<void, int>::CreateRejected(FROM_HERE).ThenOnCurrent(
+          FROM_HERE, BindOnce([]() {}));
+
+  RunLoop().RunUntilIdle();
+
+  Promise<void, int> null_promise;
+  EXPECT_DCHECK_DEATH({ p = null_promise; });
+
+  // EXPECT_DCHECK_DEATH uses fork under the hood so we still have to tidy up.
+  p.CatchOnCurrent(FROM_HERE, BindOnce([]() {}));
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverPotentialUnhandledRejection) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void, void> promise_resolver(FROM_HERE);
+
+  // |promise_resolver| could reject but there's no catch.
+  Promise<void, void> p =
+      promise_resolver.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() {}));
+
+  promise_resolver.Resolve();
+  RunLoop().RunUntilIdle();
+
+  Promise<void, void> null_promise;
+  EXPECT_DCHECK_DEATH({ p = null_promise; });
+
+  // EXPECT_DCHECK_DEATH uses fork under the hood so we still have to tidy up.
+  p.CatchOnCurrent(FROM_HERE, BindOnce([]() {}));
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverResolveCalledTwice) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void> promise_resolver(FROM_HERE);
+
+  promise_resolver.Resolve();
+
+  EXPECT_DCHECK_DEATH({ promise_resolver.Resolve(); });
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverRejectCalledTwice) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void, void> promise_resolver(
+      FROM_HERE, RejectPolicy::kCatchNotRequired);
+
+  promise_resolver.Reject();
+
+  EXPECT_DCHECK_DEATH({ promise_resolver.Reject(); });
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverResolveCalledAfterReject) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void, void> promise_resolver(
+      FROM_HERE, RejectPolicy::kCatchNotRequired);
+
+  promise_resolver.Reject();
+
+  EXPECT_DCHECK_DEATH({ promise_resolver.Resolve(); });
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverRepeatingResolveCallbackCalledTwice) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void, void> promise_resolver(
+      FROM_HERE, RejectPolicy::kCatchNotRequired);
+  RepeatingCallback<void(void)> resolve =
+      promise_resolver.GetRepeatingResolveCallback();
+
+  resolve.Run();
+
+  EXPECT_DCHECK_DEATH({ resolve.Run(); });
+#endif
+}
+
+TEST_F(PromiseTest, ManualPromiseResolverRepeatingRejectCallbackCalledTwice) {
+#if DCHECK_IS_ON()
+  ManualPromiseResolver<void, void> promise_resolver(
+      FROM_HERE, RejectPolicy::kCatchNotRequired);
+  RepeatingCallback<void(void)> resolve =
+      promise_resolver.GetRepeatingRejectCallback();
+
+  resolve.Run();
+
+  EXPECT_DCHECK_DEATH({ resolve.Run(); });
+#endif
+}
+
+class MultiThreadedPromiseTest : public PromiseTest {
+ public:
+  void SetUp() override {
+    thread_a_.reset(new Thread("MultiThreadPromiseTest_Thread_A"));
+    thread_b_.reset(new Thread("MultiThreadPromiseTest_Thread_B"));
+    thread_c_.reset(new Thread("MultiThreadPromiseTest_Thread_C"));
+    thread_a_->Start();
+    thread_b_->Start();
+    thread_c_->Start();
+  }
+
+  void TearDown() override {
+    thread_a_->Stop();
+    thread_b_->Stop();
+    thread_c_->Stop();
+  }
+
+  std::unique_ptr<Thread> thread_a_;
+  std::unique_ptr<Thread> thread_b_;
+  std::unique_ptr<Thread> thread_c_;
+};
+
+TEST_F(MultiThreadedPromiseTest, SimpleThreadHopping) {
+  ManualPromiseResolver<void> promise_resolver(FROM_HERE);
+
+  RunLoop run_loop;
+  promise_resolver.promise()
+      .ThenOn(
+          thread_a_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() {
+            EXPECT_TRUE(thread_a_->task_runner()->RunsTasksInCurrentSequence());
+          }))
+      .ThenOn(
+          thread_b_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() {
+            EXPECT_TRUE(thread_b_->task_runner()->RunsTasksInCurrentSequence());
+          }))
+      .ThenOn(
+          thread_c_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() {
+            EXPECT_TRUE(thread_c_->task_runner()->RunsTasksInCurrentSequence());
+          }))
+      .ThenOnCurrent(
+          FROM_HERE, BindLambdaForTesting([&]() {
+            EXPECT_FALSE(
+                thread_a_->task_runner()->RunsTasksInCurrentSequence());
+            EXPECT_FALSE(
+                thread_b_->task_runner()->RunsTasksInCurrentSequence());
+            EXPECT_FALSE(
+                thread_c_->task_runner()->RunsTasksInCurrentSequence());
+            run_loop.Quit();
+          }));
+
+  promise_resolver.Resolve();
+  run_loop.Run();
+}
+
+TEST_F(MultiThreadedPromiseTest, CrossThreadThens) {
+  ManualPromiseResolver<void> promise_resolver(FROM_HERE);
+  std::atomic<int> count(0);
+
+  auto increment_task =
+      BindLambdaForTesting([&]() { std::atomic_fetch_add(&count, 1); });
+
+  auto resolve_task =
+      BindLambdaForTesting([&]() { promise_resolver.Resolve(); });
+
+  RunLoop run_loop;
+  thread_a_->task_runner()->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        // Post 500 thens.
+        for (int i = 0; i < 500; i++) {
+          promise_resolver.promise().ThenOn(thread_c_->task_runner(), FROM_HERE,
+                                            increment_task);
+        }
+        // Post a task onto the main thread to resolve |promise_resolver|.
+        // This should run at an undefined time yet all the thens should run.
+        thread_b_->task_runner()->PostTask(FROM_HERE, resolve_task);
+
+        // Post another 500 thens.
+        for (int i = 0; i < 500; i++) {
+          promise_resolver.promise().ThenOn(thread_c_->task_runner(), FROM_HERE,
+                                            increment_task);
+        }
+        promise_resolver.promise().ThenOn(thread_c_->task_runner(), FROM_HERE,
+                                          run_loop.QuitClosure());
+      }));
+  run_loop.Run();
+
+  EXPECT_EQ(std::atomic_load(&count), 1000);
+}
+
+TEST_F(PromiseTest, ThreadPoolThenChain) {
+  ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE);
+  auto main_sequence = SequencedTaskRunnerHandle::Get();
+
+  RunLoop run_loop;
+  p.promise()
+      .ThenOn({TaskPriority::USER_BLOCKING}, FROM_HERE,
+              BindLambdaForTesting([&](std::vector<size_t> result) {
+                EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence());
+                result.push_back(1);
+                return result;
+              }))
+      .ThenOn({TaskPriority::USER_BLOCKING}, FROM_HERE,
+              BindLambdaForTesting([&](std::vector<size_t> result) {
+                EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence());
+                result.push_back(2);
+                return result;
+              }))
+      .ThenOn({TaskPriority::USER_BLOCKING}, FROM_HERE,
+              BindLambdaForTesting([&](std::vector<size_t> result) {
+                EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence());
+                result.push_back(3);
+                return result;
+              }))
+      .ThenOnCurrent(FROM_HERE,
+                     BindLambdaForTesting([&](std::vector<size_t> result) {
+                       EXPECT_TRUE(main_sequence->RunsTasksInCurrentSequence());
+                       EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
+                       run_loop.Quit();
+                     }));
+
+  p.Resolve(std::vector<size_t>{0});
+  run_loop.Run();
+}
+
+}  // namespace base
diff --git a/base/task/promise/promise_unittest.nc b/base/task/promise/promise_unittest.nc
new file mode 100644
index 0000000..078530a
--- /dev/null
+++ b/base/task/promise/promise_unittest.nc
@@ -0,0 +1,105 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/task/promise/promise.h"
+#include "base/task/promise/promise_result.h"
+
+namespace base {
+
+#if defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
+void WontCompile() {
+  Promise<int> p;
+  p.CatchOnCurrent(FROM_HERE, BindOnce([]() {}));
+}
+#elif defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE_TYPE_TWO) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
+void WontCompile() {
+  Promise<int> p;
+  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) {}), BindOnce([]() {}));
+}
+#elif defined(NCTEST_METHOD_RESOLVE_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_resolve| callback must accept Promise::ResolveType or void\."]
+void WontCompile() {
+  Promise<int, void> p;
+  p.ThenOnCurrent(FROM_HERE, BindOnce([](bool) { }));
+}
+#elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
+void WontCompile() {
+  Promise<int, void> p;
+  p.CatchOnCurrent(FROM_HERE, BindOnce([](bool) { }));
+}
+#elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH_TYPE_TWO) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
+void WontCompile() {
+  Promise<int, void> p;
+  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { }), BindOnce([](bool) { }));
+}
+#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
+void WontCompile() {
+  Promise<void> p;
+  p.ThenOnCurrent(
+      FROM_HERE,
+      BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
+      BindOnce([](int err) -> Rejected<bool> { return "123"; }));
+}
+#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES2) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
+void WontCompile() {
+  Promise<void> p;
+  p.ThenOnCurrent(
+      FROM_HERE,
+      BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
+      BindOnce([](int err) -> Resolved<std::string> { return "123"; }));
+}
+#elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES3) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
+void WontCompile() {
+  Promise<int, void> p;
+  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { return true; }),
+                             BindOnce([](int) { return 123.0; }));
+}
+#elif defined(NCTEST_METHOD_AMBIGUOUS_CONSTRUCTOR) // [r"fatal error: static_assert failed .*\"Ambiguous because ResolveType and RejectType are the same"]
+void WontCompile() {
+  PromiseResult<void, void> pr;
+}
+#elif defined(NCTEST_METHOD_AMBIGUOUS_CONSTRUCTOR2) // [r"fatal error: static_assert failed .*\"Ambiguous because ResolveType and RejectType are the same"]
+void WontCompile() {
+  PromiseResult<int, int> pr(123);
+}
+#elif defined(NCTEST_METHOD_REJECTED_NOREJECT) // [r"fatal error: static_assert failed .*\"Can't have Rejected<NoReject>"]
+void WontCompile() {
+  Rejected<NoReject>();
+}
+#elif defined(NCTEST_METHOD_ARGUMENT_DOESNT_MATCH) // [r"fatal error: static_assert failed .*\"Argument matches neither resolve nor reject type\."]
+void WontCompile() {
+  PromiseResult<int, float> pr("invalid");
+}
+#elif defined(NCTEST_METHOD_ARGUMENT_DOESNT_MATCH2) // [r"fatal error: static_assert failed .*\"Promise resolve types don't match"]
+void WontCompile() {
+  Promise<void> p;
+  PromiseResult<int, float> pr(p);
+}
+#elif defined(NCTEST_METHOD_UNRELATED_RESOLVE) // [r"fatal error: static_assert failed .*\"T in Resolved<T> is not ResolveType"]
+void WontCompile() {
+  struct Unrelated{};
+  PromiseResult<int, void> pr(Resolved<Unrelated>{});
+}
+#elif defined(NCTEST_METHOD_UNRELATED_REJECT) // [r"fatal error: static_assert failed .*\"T in Rejected<T> is not RejectType"]
+void WontCompile() {
+  struct Unrelated{};
+  PromiseResult<int, void> pr(Rejected<Unrelated>{});
+}
+#elif defined(NCTEST_METHOD_AMBIGUOUS_REJECT_TYPE) // [r"fatal error: static_assert failed .*\"Ambiguous promise reject type"]
+void WontCompile() {
+  Promise<int, void> p1;
+  // If supported |p2| would have type Promise<NoReject, variant<void, bool>>.
+  auto p2 = p1.ThenOnCurrent(FROM_HERE, BindOnce([]() { return Rejected<bool>(true); }));
+}
+#elif defined(NCTEST_METHOD_AMBIGUOUS_RESOLVE_TYPE) // [r"fatal error: static_assert failed .*\"Ambiguous promise resolve type"]
+void WontCompile() {
+  Promise<int, void> p1;
+  // If supported |p2| would have type Promise<variant<int, bool>, NoReject>.
+  auto p2 = p1.CatchOnCurrent(FROM_HERE, BindOnce([](int) { return Resolved<bool>(true); }));
+}
+#endif
+
+}  // namespace base
diff --git a/base/task/promise/then_and_catch_executor.cc b/base/task/promise/then_and_catch_executor.cc
new file mode 100644
index 0000000..ac8c230
--- /dev/null
+++ b/base/task/promise/then_and_catch_executor.cc
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/promise/then_and_catch_executor.h"
+
+namespace base {
+namespace internal {
+
+BaseThenAndCatchExecutor::BaseThenAndCatchExecutor(
+    internal::CallbackBase&& resolve_executor,
+    internal::CallbackBase&& reject_executor)
+    : resolve_callback_(std::move(resolve_executor)),
+      reject_callback_(std::move(reject_executor)) {}
+
+BaseThenAndCatchExecutor::~BaseThenAndCatchExecutor() = default;
+
+bool BaseThenAndCatchExecutor::IsCancelled() const {
+  if (!resolve_callback_.is_null()) {
+    // If there is both a resolve and a reject executor they must be canceled
+    // at the same time.
+    DCHECK(reject_callback_.is_null() ||
+           reject_callback_.IsCancelled() == resolve_callback_.IsCancelled());
+    return resolve_callback_.IsCancelled();
+  }
+  return reject_callback_.IsCancelled();
+}
+
+AbstractPromise::Executor::PrerequisitePolicy
+BaseThenAndCatchExecutor::GetPrerequisitePolicy() {
+  return PrerequisitePolicy::kAll;
+}
+
+void BaseThenAndCatchExecutor::Execute(AbstractPromise* promise) {
+  AbstractPromise* prerequisite =
+      promise->GetOnlyPrerequisite()->FindNonCurriedAncestor();
+  if (prerequisite->IsResolved()) {
+    if (ProcessNullCallback(resolve_callback_, prerequisite, promise)) {
+      promise->OnResolved();
+      return;
+    }
+
+    ExecuteThen(prerequisite, promise);
+  } else {
+    DCHECK(prerequisite->IsRejected());
+    if (ProcessNullCallback(reject_callback_, prerequisite, promise)) {
+      promise->OnResolved();
+      return;
+    }
+
+    ExecuteCatch(prerequisite, promise);
+  }
+}
+
+// static
+bool BaseThenAndCatchExecutor::ProcessNullCallback(const CallbackBase& callback,
+                                                   AbstractPromise* arg,
+                                                   AbstractPromise* result) {
+  if (callback.is_null()) {
+    // A curried promise is used to forward the result through null callbacks.
+    result->emplace(scoped_refptr<AbstractPromise>(arg));
+    DCHECK(result->IsResolvedWithPromise());
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/then_and_catch_executor.h b/base/task/promise/then_and_catch_executor.h
new file mode 100644
index 0000000..64f0556
--- /dev/null
+++ b/base/task/promise/then_and_catch_executor.h
@@ -0,0 +1,209 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
+#define BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
+
+#include <type_traits>
+
+#include "base/callback.h"
+#include "base/task/promise/abstract_promise.h"
+#include "base/task/promise/helpers.h"
+
+namespace base {
+namespace internal {
+
+// Exists to reduce template bloat.
+class BASE_EXPORT BaseThenAndCatchExecutor : public AbstractPromise::Executor {
+ public:
+  BaseThenAndCatchExecutor(CallbackBase&& resolve_callback,
+                           CallbackBase&& reject_callback);
+
+  ~BaseThenAndCatchExecutor() override;
+
+  // AbstractPromise::Executor:
+  bool IsCancelled() const override;
+  PrerequisitePolicy GetPrerequisitePolicy() override;
+  void Execute(AbstractPromise* promise) override;
+
+ protected:
+  virtual void ExecuteThen(AbstractPromise* prerequisite,
+                           AbstractPromise* promise) = 0;
+
+  virtual void ExecuteCatch(AbstractPromise* prerequisite,
+                            AbstractPromise* promise) = 0;
+
+  // If |executor| is null then the value of |arg| is moved or copied into
+  // |result| and true is returned. Otherwise false is returned.
+  static bool ProcessNullCallback(const CallbackBase& executor,
+                                  AbstractPromise* arg,
+                                  AbstractPromise* result);
+
+  CallbackBase resolve_callback_;
+  CallbackBase reject_callback_;
+};
+
+// Tag signals no callback which is used to eliminate dead code.
+struct NoCallback {};
+
+struct CouldResolveOrReject {};
+struct CanOnlyResolve {};
+struct CanOnlyReject {};
+
+template <bool can_resolve, bool can_reject>
+struct CheckResultHelper;
+
+template <>
+struct CheckResultHelper<true, false> {
+  using TagType = CanOnlyResolve;
+};
+
+template <>
+struct CheckResultHelper<true, true> {
+  using TagType = CouldResolveOrReject;
+};
+
+template <>
+struct CheckResultHelper<false, true> {
+  using TagType = CanOnlyReject;
+};
+
+template <typename ResolveOnceCallback,
+          typename RejectOnceCallback,
+          typename ArgResolve,
+          typename ArgReject,
+          typename ResolveStorage,
+          typename RejectStorage>
+class ThenAndCatchExecutor : public BaseThenAndCatchExecutor {
+ public:
+  using ResolveReturnT =
+      typename CallbackTraits<ResolveOnceCallback>::ReturnType;
+  using RejectReturnT = typename CallbackTraits<RejectOnceCallback>::ReturnType;
+  using PrerequisiteCouldResolve =
+      std::integral_constant<bool,
+                             !std::is_same<ArgResolve, NoCallback>::value>;
+  using PrerequisiteCouldReject =
+      std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>;
+
+  ThenAndCatchExecutor(ResolveOnceCallback&& resolve_callback,
+                       RejectOnceCallback&& reject_callback)
+      : BaseThenAndCatchExecutor(std::move(resolve_callback),
+                                 std::move(reject_callback)) {
+    static_assert(sizeof(CallbackBase) == sizeof(ResolveOnceCallback),
+                  "We assume it's possible to cast from CallbackBase to "
+                  "ResolveOnceCallback");
+    static_assert(sizeof(CallbackBase) == sizeof(RejectOnceCallback),
+                  "We assume it's possible to cast from CallbackBase to "
+                  "RejectOnceCallback");
+  }
+
+  ~ThenAndCatchExecutor() override = default;
+
+  void ExecuteThen(AbstractPromise* prerequisite,
+                   AbstractPromise* promise) override {
+    ExecuteThenInternal(prerequisite, promise, PrerequisiteCouldResolve());
+  }
+
+  void ExecuteCatch(AbstractPromise* prerequisite,
+                    AbstractPromise* promise) override {
+    ExecuteCatchInternal(prerequisite, promise, PrerequisiteCouldReject());
+  }
+
+#if DCHECK_IS_ON()
+  ArgumentPassingType ResolveArgumentPassingType() const override {
+    return resolve_callback_.is_null()
+               ? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
+               : CallbackTraits<ResolveOnceCallback>::argument_passing_type;
+  }
+
+  ArgumentPassingType RejectArgumentPassingType() const override {
+    return reject_callback_.is_null()
+               ? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
+               : CallbackTraits<RejectOnceCallback>::argument_passing_type;
+  }
+
+  bool CanResolve() const override {
+    return (!resolve_callback_.is_null() &&
+            PromiseCallbackTraits<ResolveReturnT>::could_resolve) ||
+           (!reject_callback_.is_null() &&
+            PromiseCallbackTraits<RejectReturnT>::could_resolve);
+  }
+
+  bool CanReject() const override {
+    return (!resolve_callback_.is_null() &&
+            PromiseCallbackTraits<ResolveReturnT>::could_reject) ||
+           (!reject_callback_.is_null() &&
+            PromiseCallbackTraits<RejectReturnT>::could_reject);
+  }
+#endif
+
+ private:
+  void ExecuteThenInternal(AbstractPromise* prerequisite,
+                           AbstractPromise* promise,
+                           std::true_type can_resolve) {
+    ResolveOnceCallback* resolve_callback =
+        static_cast<ResolveOnceCallback*>(&resolve_callback_);
+    RunHelper<ResolveOnceCallback, Resolved<ArgResolve>, ResolveStorage,
+              RejectStorage>::Run(std::move(*resolve_callback), prerequisite,
+                                  promise);
+
+    using CheckResultTagType = typename CheckResultHelper<
+        PromiseCallbackTraits<ResolveReturnT>::could_resolve,
+        PromiseCallbackTraits<ResolveReturnT>::could_reject>::TagType;
+
+    CheckResultType(promise, CheckResultTagType());
+  }
+
+  void ExecuteThenInternal(AbstractPromise* prerequisite,
+                           AbstractPromise* promise,
+                           std::false_type can_resolve) {
+    // |prerequisite| can't resolve so don't generate dead code.
+  }
+
+  void ExecuteCatchInternal(AbstractPromise* prerequisite,
+                            AbstractPromise* promise,
+                            std::true_type can_reject) {
+    RejectOnceCallback* reject_callback =
+        static_cast<RejectOnceCallback*>(&reject_callback_);
+    RunHelper<RejectOnceCallback, Rejected<ArgReject>, ResolveStorage,
+              RejectStorage>::Run(std::move(*reject_callback), prerequisite,
+                                  promise);
+
+    using CheckResultTagType = typename CheckResultHelper<
+        PromiseCallbackTraits<RejectReturnT>::could_resolve,
+        PromiseCallbackTraits<RejectReturnT>::could_reject>::TagType;
+
+    CheckResultType(promise, CheckResultTagType());
+  }
+
+  void ExecuteCatchInternal(AbstractPromise* prerequisite,
+                            AbstractPromise* promise,
+                            std::false_type can_reject) {
+    // |prerequisite| can't reject so don't generate dead code.
+  }
+
+  void CheckResultType(AbstractPromise* promise, CouldResolveOrReject) {
+    if (promise->IsResolvedWithPromise() ||
+        promise->value().type() == TypeId::From<ResolveStorage>()) {
+      promise->OnResolved();
+    } else {
+      DCHECK_EQ(promise->value().type(), TypeId::From<RejectStorage>())
+          << " See " << promise->from_here().ToString();
+      promise->OnRejected();
+    }
+  }
+
+  void CheckResultType(AbstractPromise* promise, CanOnlyResolve) {
+    promise->OnResolved();
+  }
+
+  void CheckResultType(AbstractPromise* promise, CanOnlyReject) {
+    promise->OnRejected();
+  }
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_THEN_AND_CATCH_EXECUTOR_H_
diff --git a/base/task_runner.cc b/base/task_runner.cc
index 0bd9ba9..d8644a9 100644
--- a/base/task_runner.cc
+++ b/base/task_runner.cc
@@ -40,8 +40,40 @@
   return destination_->PostTask(from_here, std::move(task));
 }
 
+// TODO(alexclarke): Remove this when TaskRunner::PostPromiseInternal becomes
+// pure virtual.
+class PromiseHolder {
+ public:
+  explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise)
+      : promise_(std::move(promise)) {}
+
+  ~PromiseHolder() {
+    // Detect if the promise was not executed and if so cancel to ensure memory
+    // is released.
+    if (promise_)
+      promise_->OnCanceled();
+  }
+
+  PromiseHolder(PromiseHolder&& other) : promise_(std::move(other.promise_)) {}
+
+  scoped_refptr<internal::AbstractPromise> Unwrap() const {
+    return std::move(promise_);
+  }
+
+ private:
+  mutable scoped_refptr<internal::AbstractPromise> promise_;
+};
+
 }  // namespace
 
+template <>
+struct BindUnwrapTraits<PromiseHolder> {
+  static scoped_refptr<internal::AbstractPromise> Unwrap(
+      const PromiseHolder& o) {
+    return o.Unwrap();
+  }
+};
+
 bool TaskRunner::PostTask(const Location& from_here, OnceClosure task) {
   return PostDelayedTask(from_here, std::move(task), base::TimeDelta());
 }
@@ -58,7 +90,8 @@
     base::TimeDelta delay) {
   return PostDelayedTask(
       promise->from_here(),
-      BindOnce(&internal::AbstractPromise::Execute, std::move(promise)), delay);
+      BindOnce(&internal::AbstractPromise::Execute, PromiseHolder(promise)),
+      delay);
 }
 
 TaskRunner::TaskRunner() = default;