当前位置: 首页 > news >正文

基于C++11手撸前端Promise——从异步编程到现代C++实践


引言

在现代前端开发中,Promise 是处理异步操作的核心工具(如 fetchsetTimeout 的链式调用),它通过 .then() 和 .catch() 方法解决了传统回调地狱(Callback Hell)问题。但鲜为人知的是,Promise 的核心思想(状态机 + 回调队列)完全可以用其他语言实现——例如用 C++11 手动构建一个类 Promise 的异步封装。本文将深入探讨如何基于 C++11 特性(如 std::functionstd::futurestd::mutex)手撸一个简化版 Promise,解析其关键概念与核心技巧,并通过详细代码分析展示实现逻辑。


一、关键概念:Promise 的本质与 C++ 映射

前端 Promise 的核心是 状态机(pending/fulfilled/rejected)和 回调队列(成功/失败的回调函数列表)。当 Promise 处于 pending 状态时,异步操作未完成;一旦完成(成功或失败),状态不可逆地转变为 fulfilled 或 rejected,并依次执行对应队列中的回调。

在 C++ 中,我们可以通过以下方式映射这些概念:

  • 状态:用枚举类型 State { PENDING, FULFILLED, REJECTED } 表示。
  • 值/错误:用 std::any(或模板化的 T 和 std::exception_ptr)存储成功结果或异常。
  • 回调队列:用 std::vector<std::function<void(T)>>(成功回调)和 std::vector<std::function<void(std::exception_ptr)>>(失败回调)存储待执行的函数。
  • 异步调度:通过 std::async 或手动线程池模拟事件循环(前端 Promise 依赖浏览器的微任务队列,C++ 中需自行实现异步触发)。

二、核心技巧:C++11 的关键技术点

实现过程中,我们需要依赖 C++11 的以下特性:

  1. std::function:封装任意可调用对象(如 lambda、成员函数),用于存储回调函数。
  2. std::future 和 std::promise(标准库工具,仅作参考):虽然我们会手写 Promise,但标准库的 std::promise 可提供异步结果传递的思路(不过本文不直接使用它,而是完全手写)。
  3. std::mutex 和 std::lock_guard:保护共享状态(如当前状态、回调队列)的线程安全(因为异步回调可能在不同线程触发)。
  4. 模板编程:支持泛型结果类型(如 Promise<int>Promise<std::string>)。

三、应用场景:为什么需要 C++ 版 Promise?

虽然前端是 Promise 的主要战场,但在 C++ 中也有类似需求场景:

  • 跨线程异步通信:例如主线程发起网络请求,子线程处理数据后通过 Promise 通知主线程结果。
  • 复杂异步流程控制:如多个异步操作依赖(类似前端的 Promise.all)。
  • 教学与理解:通过手写实现深入掌握状态机和回调队列的设计模式。

四、详细代码案例分析(核心实现)

下面是一个简化版 Promise<T> 的完整实现(支持 then 链式调用和基本状态管理),代码超过 500 字的详细注释分析如下:

#include <iostream>
#include <functional>
#include <vector>
#include <memory>
#include <mutex>
#include <any>
#include <exception>
#include <stdexcept>// 定义 Promise 的三种状态
enum class State { PENDING, FULFILLED, REJECTED };// 前向声明
template<typename T>
class Promise;// 内部状态管理类(封装实际逻辑)
template<typename T>
class PromiseState {
private:State state;                          // 当前状态(初始为 PENDING)std::any value;                       // 成功时的结果(类型擦除,用 any 存储)std::exception_ptr error;             // 失败时的异常指针std::vector<std::function<void(T)>> onFulfilledCallbacks; // 成功回调队列std::vector<std::function<void(std::exception_ptr)>> onRejectedCallbacks; // 失败回调队列std::mutex mtx;                       // 互斥锁保护共享数据public:PromiseState() : state(State::PENDING) {}// 设置成功结果并触发回调void resolve(T val) {std::lock_guard<std::mutex> lock(mtx);if (state != State::PENDING) return; // 状态不可逆state = State::FULFILLED;value = val;// 依次执行所有成功回调for (auto& cb : onFulfilledCallbacks) {try {cb(std::any_cast<T>(value)); // 擦除类型恢复} catch (const std::bad_any_cast& e) {// 类型不匹配时触发拒绝(简化处理)reject(std::make_exception_ptr(e));}}onFulfilledCallbacks.clear(); // 清空已执行的回调}// 设置失败结果并触发回调void reject(std::exception_ptr err) {std::lock_guard<std::mutex> lock(mtx);if (state != State::PENDING) return;state = State::REJECTED;error = err;// 依次执行所有失败回调for (auto& cb : onRejectedCallbacks) {cb(error);}onRejectedCallbacks.clear();}// 注册成功回调(返回新的 Promise 以支持链式调用,此处简化)void then(std::function<void(T)> onFulfilled) {std::lock_guard<std::mutex> lock(mtx);if (state == State::FULFILLED) {// 当前已成功,直接同步执行回调try {onFulfilled(std::any_cast<T>(value));} catch (const std::bad_any_cast& e) {// 实际应用中应处理异常std::cerr << "Type cast error in then: " << e.what() << std::endl;}} else if (state == State::PENDING) {// 当前未完成,加入回调队列onFulfilledCallbacks.push_back(onFulfilled);}// 若已拒绝,忽略(简化实现,实际应支持失败回调链)}// 注册失败回调void catch_(std::function<void(std::exception_ptr)> onRejected) {std::lock_guard<std::mutex> lock(mtx);if (state == State::REJECTED) {onRejected(error);} else if (state == State::PENDING) {onRejectedCallbacks.push_back(onRejected);}}
};// Promise 主类(对外接口)
template<typename T>
class Promise {
private:std::shared_ptr<PromiseState<T>> state; // 共享状态(通过智能指针管理生命周期)public:// 构造函数(初始化 pending 状态)Promise() : state(std::make_shared<PromiseState<T>>()) {}// 模拟异步操作(例如网络请求),完成后调用 resolve/rejectvoid executeAsync(std::function<void(std::function<void(T)>, std::function<void(std::exception_ptr)>)> asyncTask) {// 在实际场景中,asyncTask 可能是网络请求、文件读取等异步操作// 这里用 lambda 模拟异步逻辑(例如延时后成功)std::thread( {asyncTask(T val { state->resolve(val); }, // 成功时调用 resolvestd::exception_ptr err { state->reject(err); } // 失败时调用 reject);}).detach(); // 分离线程(模拟异步执行)}// then 方法(链式调用的基础)Promise<T> then(std::function<void(T)> onFulfilled) {state->then(onFulfilled);return *this; // 返回自身以支持链式(简化,实际应返回新 Promise)}// catch 方法(错误处理)Promise<T> catch_(std::function<void(std::exception_ptr)> onRejected) {state->catch_(onRejected);return *this;}
};// 示例:使用手写 Promise 模拟异步获取数据
int main() {Promise<int> promise;// 模拟异步任务:2 秒后返回结果 42(或抛出异常)promise.executeAsync(std::function<void(int> resolve, std::function<void(std::exception_ptr)> reject) {std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作bool success = true; // 假设操作可能失败if (success) {resolve(42); // 成功时传递结果} else {reject(std::make_exception_ptr(std::runtime_error("模拟异步失败"))); // 失败时传递异常}});// 注册成功回调promise.then(int result {std::cout << "异步操作成功!结果: " << result << std::endl;});// 注册失败回调promise.catch_(std::exception_ptr err {try {std::rethrow_exception(err);} catch (const std::exception& e) {std::cout << "异步操作失败!错误: " << e.what() << std::endl;}});// 防止主线程提前退出(等待异步操作完成)std::this_thread::sleep_for(std::chrono::seconds(3));return 0;
}
代码分析(重点部分,超 500 字)
  1. 状态管理类 PromiseState<T>:这是核心逻辑的载体,封装了 Promise 的所有状态(state)、结果值(value)、异常(error)以及回调队列(onFulfilledCallbacks 和 onRejectedCallbacks)。通过 std::mutex 保证多线程环境下对共享数据的访问安全(例如多个线程同时调用 then 或异步任务触发 resolve)。

    • resolve(T val) 方法:当异步操作成功时调用,首先检查当前状态是否为 PENDING(确保状态不可逆),然后将状态置为 FULFILLED,存储结果值,并遍历执行所有已注册的成功回调(通过 std::any_cast 恢复类型信息)。
    • reject(std::exception_ptr err) 方法:类似地,处理失败情况,存储异常指针并触发所有失败回调。
    • then 和 catch_ 方法:分别用于注册成功和失败的回调函数。如果当前状态已经是 FULFILLED 或 REJECTED,则直接同步执行回调;否则将回调加入队列,等待异步任务完成后触发。
  2. 主类 Promise<T>:对外暴露简洁接口,内部通过 std::shared_ptr 共享 PromiseState<T> 实例(确保状态生命周期与 Promise 对象一致)。executeAsync 方法模拟异步任务的执行(例如网络请求),接受一个 lambda 参数,该 lambda 提供 resolve 和 reject 函数,供异步操作完成后调用。

    • 用户通过 then 注册成功回调(例如打印结果),通过 catch_ 注册失败回调(例如打印错误信息)。
  3. 示例场景:在 main 函数中,创建了一个 Promise<int> 实例,模拟一个耗时 2 秒的异步操作(通过 std::this_thread::sleep_for)。操作成功时返回结果 42,失败时抛出异常。通过 then 和 catch_ 注册回调后,主线程休眠 3 秒等待异步操作完成(实际项目中应使用更优雅的同步机制,如条件变量)。


五、未来发展趋势

虽然 C++ 不像前端那样依赖 Promise 处理大量 UI 异步交互,但在以下方向仍有潜力:

  • 协程支持(C++20 起):结合 co_await 和自定义 Promise-like 对象,可以更自然地实现异步流程控制(例如替代回调嵌套)。
  • 跨语言交互:在 C++ 与 JavaScript 混合开发(如 Electron、WebAssembly)中,手写 Promise 可作为桥梁统一异步逻辑。
  • 嵌入式/高性能场景:对于资源受限但需要异步处理的系统(如物联网设备),轻量级 Promise 封装比线程池更高效。

http://www.dtcms.com/a/392621.html

相关文章:

  • 构建AI智能体:三十九、中文新闻智能分类:K-Means聚类与Qwen主题生成的融合应用
  • [vibe code追踪] 程序列表视图 | renderNodeList
  • 解决 `sudo rosdepc init` 报错:`command not found` 的完整指南
  • 大数据毕业设计选题推荐-基于大数据的气候驱动的疾病传播可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • Maven 实战:多模块项目与高级打包配置
  • AI 精准绘图专栏:从描述到图像,让创意精准落地​
  • 基于C++11手撸前端Promise进阶——链式调用与组合操作(All/Race)的实现
  • 美国批准通用上市标准!加密货币ETF即将爆发?
  • 子查询及其分类
  • MySQL的存储引擎(一条sql语句的执行流程是什么样的?)
  • JavaScript学习笔记(二):遍历方法汇总
  • Ubuntu22.04显卡掉驱动,重装命令
  • 模式组合应用-享元模式
  • 租房小程序房产小程序源码方案详解
  • p-value与e-value
  • 面经分享--京东一面
  • 大数据毕业设计选题推荐-基于大数据的帕金森病数据可视化分析系统-Spark-Hadoop-Bigdata
  • stack 和 queue
  • 执行yarn init报错:error Invalid package name.(question name)包名格式不对
  • Windows 下 PyTorch 入门深度学习环境安装与配置 CPU GPU 版 | 土堆教程
  • Transformer中为什么要使用多头注意力?
  • 《嵌入式硬件(十六):基于IMX6ULL的I2C的操作》
  • AI.工作助手.工作提效率
  • 【开题答辩全过程】以 Louis宠物商城为例,包含答辩的问题和答案
  • 微服务-网络模型与服务通信方式openfein
  • 如何快速定位局域网丢包设备?
  • 算法<java>——排序(冒泡、插入、选择、归并、快速、计数、堆、桶、基数)
  • 深入浅出CMMI:从混乱到卓越的研发管理体系化之路
  • Docker一键部署prometheus并实现飞书告警详解
  • 基于“开源AI大模型AI智能名片S2B2C商城小程序”的多平台资源位传播对直播营销流量转化的影响研究