目录

引言

前端Promise的应用与优势

手写C++ Promise实现

std::promise与CPromise对比

新增内容:线程安全与性能优化

总结与展望


引言

在前端开发中,Promise作为处理异步操作的核心工具,显著提升了代码的可读性和可维护性。其概念并非局限于JavaScript领域,在C++11标准中也引入了std::promise,为开发者提供了类似的异步处理机制。本文将基于C++11实现一个手写Promise类(命名为CPromise),深入剖析其设计原理,并通过与std::promise的对比,探讨两者的异同点及适用场景。文章旨在帮助开发者理解异步编程的本质,并为实际项目提供参考。

前端Promise的应用与优势

Promise在前端开发中广泛应用于处理异步任务,其优势主要体现在以下几个方面:

常见应用场景

  1. 网络请求:Promise简化了AJAX请求的逻辑,例如使用fetchAPI:
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    console.log('获取到数据:', data);
  })
  .catch(error => {
    console.error('请求失败:', error);
  });

  1. 定时器处理:Promise使定时器代码更直观:
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
timeout(1000)
  .then(() => {
    console.log('1秒后执行');
  });

  1. 并发请求:通过Promise.all管理多个异步任务:
const promise1 = fetch('https://api.example.com/data1');
const promise2 = fetch('https://api.example.com/data2');
Promise.all([promise1, promise2])
  .then(responses => {
    const [data1, data2] = responses.map(response => response.json());
    return Promise.all([data1, data2]);
  })
  .then(([data1, data2]) => {
    console.log('两个数据都获取成功:', data1, data2);
  })
  .catch(error => {
    console.error('至少一个请求失败:', error);
  });

Promise解决的问题

  • 回调地狱:链式调用替代嵌套回调,提升代码结构清晰度。
  • 错误处理:统一通过.catch()捕获异常,避免分散的错误处理逻辑。
  • 并发控制Promise.allPromise.race简化多任务协同。
手写C++ Promise实现

以下基于C++11实现一个简化版Promise类CPromise,支持基本异步操作。

类结构与成员变量

template<typename T>
class CPromise {
private:
    // 定义回调函数类型
    using ResolveHandler = std::function<void(T)>;
    using RejectHandler = std::function<void(const std::string&)>;
    
    // 状态枚举
    enum class PromiseState { PENDING, FULFILLED, REJECTED };
    
    T m_value;                          // 存储结果值
    std::string m_reason;               // 拒绝原因
    PromiseState m_state;               // 当前状态
    std::list<ResolveHandler> m_resolves; // 成功回调列表
    std::list<RejectHandler> m_rejects;  // 失败回调列表

public:
    CPromise();
    void resolve(T value);
    void reject(const std::string& reason);
    CPromise* then(const ResolveHandler& handler);
    void onCatch(const RejectHandler& handler);
};

构造函数
初始化状态为PENDING

template<typename T>
CPromise<T>::CPromise() : m_state(PromiseState::PENDING) {}

resolve方法
将状态设为FULFILLED并执行回调:

template<typename T>
void CPromise<T>::resolve(T value) {
    if (m_state == PromiseState::PENDING) {
        m_value = value;
        m_state = PromiseState::FULFILLED;
        for (auto& handler : m_resolves) {
            handler(value);  // 执行所有成功回调
        }
    }
}

reject方法
将状态设为REJECTED并执行回调:

template<typename T>
void CPromise<T>::reject(const std::string& reason) {
    if (m_state == PromiseState::PENDING) {
        m_reason = reason;
        m_state = PromiseState::REJECTED;
        for (auto& handler : m_rejects) {
            handler(reason);  // 执行所有失败回调
        }
    }
}

then方法
注册成功回调,支持链式调用:

template<typename T>
CPromise<T>* CPromise<T>::then(const ResolveHandler& handler) {
    if (m_state == PromiseState::FULFILLED) {
        handler(m_value);  // 立即执行
    } else if (m_state == PromiseState::PENDING) {
        m_resolves.push_back(handler);  // 添加到队列
    }
    return this;  // 返回自身以支持链式调用
}

onCatch方法
注册失败回调:

template<typename T>
void CPromise<T>::onCatch(const RejectHandler& handler) {
    if (m_state == PromiseState::REJECTED) {
        handler(m_reason);  // 立即执行
    } else if (m_state == PromiseState::PENDING) {
        m_rejects.push_back(handler);  // 添加到队列
    }
}

链式调用示例

CPromise<int>* promise = new CPromise<int>();
promise->then([](int value) {
    std::cout << "结果: " << value << std::endl;
})->onCatch([](const std::string& reason) {
    std::cout << "错误: " << reason << std::endl;
});
promise->reject("网络异常!!!");

std::promise与CPromise对比

基础功能对比

功能 CPromise实现 std::promise
状态管理 手动实现 标准库实现
回调注册与执行 手动队列 通过future关联
异步支持 需结合线程 内置线程安全
链式调用 支持 不支持

实现细节对比

  1. 状态管理

    • CPromise:自定义状态枚举(PENDING, FULFILLED, REJECTED),需手动维护。
    • std::promise:内部管理状态,通过std::future获取结果。
  2. 回调注册

    • CPromise:使用std::list存储回调,需手动遍历执行。
    • std::promise:通过set_valueset_exception触发关联的future
  3. 线程安全

    • CPromise:非线程安全,需外部同步(如互斥锁)。
    • std::promise:线程安全,可跨线程操作。
  4. 链式调用

    • CPromise:通过返回this实现链式调用。
    • std::promise:不支持链式,需配合std::future使用。

代码示例对比

  1. CPromise示例
CPromise<std::string>* p = new CPromise<std::string>();
p->then([](std::string s) { std::cout << s; });
p->resolve("成功!");

  1. std::promise示例
std::promise<std::string> prom;
std::future<std::string> fut = prom.get_future();
std::thread t([&] { prom.set_value("结果"); });
t.join();
std::cout << fut.get();  // 输出"结果"

优缺点分析

  • CPromise

    • 优点:轻量级、支持链式调用、易于定制扩展。
    • 缺点:非线程安全、需手动管理状态。
  • std::promise

    • 优点:线程安全、标准库支持、与std::future无缝集成。
    • 缺点:不支持链式调用、使用稍复杂。
新增内容:线程安全与性能优化

为提升CPromise的实用性,可添加线程安全机制:

#include <mutex>
template<typename T>
class SafePromise : public CPromise<T> {
private:
    std::mutex m_mtx;
public:
    void resolve(T value) {
        std::lock_guard<std::mutex> lock(m_mtx);
        CPromise<T>::resolve(value);
    }
    // 类似实现reject等方法
};

性能方面,std::promise因标准库优化通常更高效,但CPromise在轻量场景下开销更低。

总结与展望

本文通过手写CPromise类,深入解析了Promise的核心机制,并对比了其与std::promise的差异。未来可扩展功能如Promise.all实现,或集成到Qt等框架中。开发者应根据需求选择:CPromise适合简单异步场景,而std::promise更适用于多线程环境。异步编程是现代化开发的基石,掌握其原理将显著提升代码质量。


版权声明:本文采用CC 4.0 BY-SA协议,转载请注明出处。
标签:C++、前端、异步编程、Promise、std::promise
专栏:基于Qt的新概念浏览器

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐