--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -588,36 +588,16 @@ public:
aThenValue->Dispatch(this);
} else {
mThenValues.AppendElement(aThenValue);
}
}
public:
- template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
- RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
- ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
- {
- RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
- aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
- ThenInternal(aResponseThread, thenValue, aCallSite);
- return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
- }
-
- template<typename ResolveFunction, typename RejectFunction>
- RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
- ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
- {
- RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
- Move(aResolveFunction), Move(aRejectFunction), aCallSite);
- ThenInternal(aResponseThread, thenValue, aCallSite);
- return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
- }
-
// ThenPromise() can be called on any thread as Then().
// The syntax is close to JS promise and makes promise chaining easier
// where you can do: p->ThenPromise()->ThenPromise()->ThenPromise();
//
// Note you would have to call Then() instead when the result needs to be held
// by a MozPromiseRequestHolder for future disconnection.
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
MOZ_MUST_USE RefPtr<MozPromise>
@@ -650,16 +630,87 @@ public:
"<completion promise>", true /* aIsCompletionPromise */);
thenValue->mCompletionPromise = p;
// Note ThenInternal() might nullify mCompletionPromise before return.
// So we need to return p instead of mCompletionPromise.
ThenInternal(aResponseThread, thenValue, aCallSite);
return p;
}
+private:
+ /*
+ * A command object to store all information needed to make a request to
+ * the promise. This allows us to delay the request until further use is
+ * known (whether it is ->Then() again for more promise chaining or passed
+ * to MozPromiseRequestHolder::Begin() to terminate chaining and issue
+ * the request).
+ *
+ * This allows a unified syntax for promise chaining and disconnection
+ * and feels more like its JS counterpart.
+ */
+ class ThenCommand
+ {
+ friend class MozPromise;
+
+ ThenCommand(AbstractThread* aResponseThread,
+ const char* aCallSite,
+ already_AddRefed<ThenValueBase> aThenValue,
+ MozPromise* aReceiver)
+ : mResponseThread(aResponseThread)
+ , mCallSite(aCallSite)
+ , mThenValue(aThenValue)
+ , mReceiver(aReceiver) {}
+
+ ThenCommand(ThenCommand&& aOther) = default;
+
+ public:
+ ~ThenCommand()
+ {
+ // Issue the request now if the return value of Then() is not used.
+ if (mThenValue) {
+ mReceiver->ThenInternal(mResponseThread, mThenValue, mCallSite);
+ }
+ }
+
+ // Allow passing Then() to MozPromiseRequestHolder::Begin().
+ operator RefPtr<Request>()
+ {
+ RefPtr<ThenValueBase> thenValue = mThenValue.forget();
+ mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
+ return thenValue.forget();
+ }
+
+ private:
+ AbstractThread* mResponseThread;
+ const char* mCallSite;
+ RefPtr<ThenValueBase> mThenValue;
+ MozPromise* mReceiver;
+ };
+
+ public:
+ template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+ ThenCommand Then(AbstractThread* aResponseThread, const char* aCallSite,
+ ThisType* aThisVal, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ {
+ using ThenType = MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>;
+ RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
+ aThisVal, aResolveMethod, aRejectMethod, aCallSite);
+ return ThenCommand(aResponseThread, aCallSite, thenValue.forget(), this);
+ }
+
+ template<typename ResolveFunction, typename RejectFunction>
+ ThenCommand Then(AbstractThread* aResponseThread, const char* aCallSite,
+ ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+ {
+ using ThenType = FunctionThenValue<ResolveFunction, RejectFunction>;
+ RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
+ Move(aResolveFunction), Move(aRejectFunction), aCallSite);
+ return ThenCommand(aResponseThread, aCallSite, thenValue.forget(), this);
+ }
+
void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
{
MutexAutoLock lock(mMutex);
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
mHaveRequest = true;
RefPtr<Private> chainedPromise = aChainedPromise;
PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
aCallSite, this, chainedPromise.get(), (int) IsPending());