#ifndef __PROMISE_H__ #define __PROMISE_H__ #include class PromiseUndefinedException : public std::exception { public: }; class PromiseConversionException : public std::exception { public: }; #include "Private/PromiseHandler.h" #include "Private/PromiseResolver.h" #include template class Promise; template class PromiseBase { friend struct PromiseFulfill>; public: using Type = T; template ::count == 1, int>::type = 0> PromiseBase(F callback) : m_d{std::make_shared>()} { PromiseResolver resolver{*this}; try { callback(PromiseResolve(resolver)); } catch (...) { resolver.reject(std::current_exception()); } } template ::count != 1, int>::type = 0> PromiseBase(F callback) : m_d{std::make_shared>()} { PromiseResolver resolver{*this}; try { callback(PromiseResolve(resolver), PromiseReject(resolver)); } catch (...) { resolver.reject(std::current_exception()); } } PromiseBase(const PromiseBase &other) : m_d{other.m_d} { } PromiseBase(const Promise &other) : m_d{other.m_d} { } PromiseBase(PromiseBase &&other) { swap(other); } virtual ~PromiseBase() { } PromiseBase &operator=(const PromiseBase &other) { m_d = other.m_d; return *this; } PromiseBase &operator=(PromiseBase &&other) { PromiseBase(std::move(other)).swap(*this); return *this; } bool operator==(const PromiseBase &other) const { return (m_d == other.m_d); } bool operator!=(const PromiseBase &other) const { return (m_d != other.m_d); } void swap(PromiseBase &other) { std::swap(m_d, other.m_d); } bool isFulfilled() const { return m_d->isFulfilled(); } bool isRejected() const { return m_d->isRejected(); } bool isPending() const { return m_d->isPending(); } template typename PromiseHandler::PromiseType then(const TFulfilled &fulfilled, const TRejected &rejected) const { using PromiseType = typename PromiseHandler::PromiseType; PromiseType next( [&](const PromiseResolve &resolve, const PromiseReject &reject) { m_d->addHandler(PromiseHandler::create(fulfilled, resolve, reject)); m_d->addCatcher(PromiseCatcher::create(rejected, resolve, reject)); }); if (!m_d->isPending()) { m_d->dispatch(); } return next; } template typename PromiseHandler::PromiseType then(TFulfilled &&fulfilled) const { return then(std::forward(fulfilled), nullptr); } template Promise finally(THandler handler) const { Promise p = *this; return p.then(handler, handler).then([=]() { return p; }); } template typename PromiseHandler::PromiseType fail(TRejected &&rejected) const { return then(nullptr, std::forward(rejected)); } Promise wait() const { while (m_d->isPending()) { std::this_thread::yield(); } return *this; } template static Promise reject(E &&error) { return Promise{[&](const PromiseResolve &, const PromiseReject &reject) { reject(std::forward(error)); }}; } template Promise tapFail(THandler handler) const { Promise p = *this; return p.then([]() {}, handler).then([=]() { return p; }); } template Promise tap(THandler handler) const { Promise p = *this; return p.then(handler).then([=]() { return p; }); } #ifdef BOOST_ASIO_EXECUTION_EXECUTOR template Promise delay(Executor &ioContext, const std::chrono::duration &duration) const { return tap([&]() { return Promise{[&](const PromiseResolve &resolve) { auto timer = std::make_shared(ioContext, duration); timer->async_wait([timer, resolve](const boost::system::error_code & /*e*/) mutable { boost::asio::post(timer->get_executor(), resolve); }); }}; }); } template Promise delay(Executor &ioContext, int milliseconds) const { return delay(ioContext, std::chrono::milliseconds(milliseconds)); } #endif protected: std::shared_ptr> m_d; }; namespace PromiseHelper { template typename PromiseDeduce::Type resolve(T &&value) { using PromiseType = typename PromiseDeduce::Type; using ValueType = typename PromiseType::Type; using ResolveType = PromiseResolve; using RejectType = PromiseReject; return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) { PromiseFulfill>::call(std::forward(value), std::forward(resolve), std::forward(reject)); }}; } Promise resolve(); template typename PromiseFunctor::PromiseType attempt(Functor &&fn, Args &&...args) { using FunctorType = PromiseFunctor; using PromiseType = typename FunctorType::PromiseType; using ValueType = typename PromiseType::Type; // NOTE: std::forward>: MSVC 2013 fails when forwarding // template type (error: "expects 4 arguments - 0 provided"). // However it succeeds with type alias. // TODO: should we expose QPromise::ResolveType & RejectType? using ResolveType = PromiseResolve; using RejectType = PromiseReject; return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) { PromiseDispatch::call(std::forward(resolve), std::forward(reject), std::forward(fn), std::forward(args)...); }}; } template class Sequence = std::vector, typename... Args> Promise> all(const Sequence, Args...> &promises) { const int count = static_cast(promises.size()); if (count == 0) { return PromiseHelper::resolve(std::vector{}); } return Promise>{[=](const PromiseResolve> &resolve, const PromiseReject> &reject) { auto remaining = std::make_shared(count); auto results = std::make_shared>(count); int i = 0; for (const auto &promise : promises) { promise.then( [=](const T &res) mutable { (*results)[i] = res; if (--(*remaining) == 0) { resolve(*results); } }, [=]() mutable { if (*remaining != -1) { *remaining = -1; reject(std::current_exception()); } }); i++; } }}; } template