跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++大前端算法

C++11 手写 Promise 实现及与 std::promise 对比

C++11 手写 Promise 实现解析,涵盖状态管理、回调注册及链式调用机制。对比标准库 std::promise,分析两者在异步支持、功能特性及适用场景上的差异。通过源码剖析理解 Promise 核心原理,为 C++ 异步编程提供参考。

落日余晖发布于 2026/4/9更新于 2026/7/1834 浏览
C++11 手写 Promise 实现及与 std::promise 对比

在前端开发中,Promise 是处理异步操作的重要工具。它通过将异步操作封装在 Promise 实例中,解决了传统回调地狱的问题,提高了代码的可读性和可维护性。虽然 Promise 的概念源自前端,但在 C++11 标准中也引入了 std::promise,用于实现类似的功能。

本文将通过一个基于 C++11 的手写 Promise 实现(命名为 CPromise),深入分析其工作原理,并与 std::promise 进行对比,探讨两者的异同点以及适用场景。

前端 Promise 的应用与优势

常见应用场景

定时器 Promise 可以很好地处理定时器,使代码更加直观:

function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

timeout(1000).then(() => {
    console.log('1 秒后执行');
});

网络请求 使用 Promise 处理 AJAX 请求,能简化异步数据获取的逻辑:

fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
        console.log('获取到数据:', data);
    })
    .catch(error => {
        console.error('请求失败:', error);
    });

并发请求

利用 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 解决的问题

  1. 回调地狱:通过链式调用,解决了传统回调嵌套导致的代码难以阅读和维护的问题。
  2. 错误处理:提供了统一的错误处理机制,通过 catch 方法可以集中处理所有异步操作中的错误。
  3. 代码可读性:使得异步代码的逻辑更加清晰,符合同步代码的书写习惯。
  4. 并发控制:通过 Promise.all 和 Promise.race,可以方便地控制多个异步操作的执行顺序和结果。

手写 C++ Promise 实现

类结构与成员变量

核心在于状态管理和回调队列。我们定义了一个模板类 CPromise:

template<typename Element>
class CPromise {
private:
    using Resolve = std::function<void(Element)>;
    using Reject = std::function<void(const std::string&)>;

private:
    Element m_element;              // 异步操作的结果
    std::string m_reason;           // 拒绝的原因
    CPromiseState m_state;          // 当前状态
    std::list<Resolve> m_resolves;  // 成功回调函数列表
    std::list<Reject> m_rejects;    // 失败回调函数列表

public:
    CPromise();
    void reject(const std::string& reason);
    void resolve(Element element);
    void onCatch(const Reject& rej);
    CPromise* then(const Resolve& res);
};

这里定义了 Resolve 和 Reject 作为回调类型,分别存储成功和失败的处理逻辑。m_state 表示 Promise 的当前状态,初始为 PENDING。m_resolves 和 m_rejects 则用于暂存注册时的回调函数。

构造函数

初始化时,状态设为 PENDING:

CPromise::CPromise() : m_state(CPromiseState::PENDING) {}

resolve 方法

当异步操作成功时,调用 resolve。如果当前处于 PENDING 状态,则更新状态并立即执行所有已注册的回调:

void CPromise::resolve(Element element) {
    m_element = element;
    if (m_state == CPromiseState::PENDING) {
        m_state = CPromiseState::FULFILLED;
        for (Resolve res : m_resolves) {
            res(element);
        }
    }
}

注意这里只允许一次状态变更,一旦完成就不再响应新的 resolve 调用。

reject 方法

类似地,处理失败情况:

void CPromise::reject(const std::string& reason) {
    m_reason = reason;
    if (m_state == CPromiseState::PENDING) {
        m_state = CPromiseState::REJECTED;
        for (Reject rej : m_rejects) {
            rej(reason);
        }
    }
}

then 方法

这是实现链式调用的关键。如果 Promise 已完成,立即执行回调;否则将回调加入队列:

CPromise* CPromise::then(const Resolve& res) {
    if (m_state == CPromiseState::FULFILLED) {
        res(m_element);
    } else if (m_state == CPromiseState::PENDING) {
        m_resolves.push_back(res);
    }
    return this;
}

返回 this 是为了支持链式调用,让代码更流畅。

onCatch 方法

用于注册错误处理回调:

void CPromise::onCatch(const Reject& rej) {
    if (m_state == CPromiseState::REJECTED) {
        rej(m_reason);
    } else if (m_state == CPromiseState::PENDING) {
        m_rejects.push_back(rej);
    }
}

链式调用与使用示例

结合上述方法,我们可以写出类似前端的链式调用:

CPromise<int>* promise = new CPromise<int>();
promise->then([](int ele) -> void {
    std::cout << ele << std::endl;
})->onCatch([](const std::string& reason) -> void {
    std::cout << reason << std::endl;
});

promise->reject("网络异常!!!");

这种写法逻辑清晰,避免了深层嵌套。

std::promise 与 CPromise 对比

基础功能对比

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

实现细节差异

状态管理:CPromise 依赖自定义枚举 CPromiseState,而 std::promise 由标准库内部处理,用户无需关心底层细节。

回调注册:CPromise 维护回调队列,通过 then 和 onCatch 灵活注册;std::promise 通常配合 std::future,通过 get 或 then 触发回调。

异步支持:CPromise 本身不处理线程,需结合 std::thread 或其他框架;std::promise 内置异步语义,常与 std::async 或 std::thread 配合。

链式调用:CPromise 支持链式调用,体验接近前端 Promise;std::promise 原生不支持链式注册回调。

代码示例对比

CPromise 示例:

CPromise<int>* promise = new CPromise<int>();
promise->then([](int ele) -> void {
    std::cout << "结果:" << ele << std::endl;
})->onCatch([](const std::string& reason) -> void {
    std::cout << "错误:" << reason << std::endl;
});
promise->resolve(42);

std::promise 示例:

#include <future>
#include <thread>
#include <iostream>

int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();

    // 异步操作
    std::thread([&prom]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        prom.set_value(42);
    }).detach();

    // 注册回调
    fut.then([](std::future<int> fut) {
        try {
            int result = fut.get();
            std::cout << "结果:" << result << std::endl;
        } catch (const std::exception& e) {
            std::cout << "错误:" << e.what() << std::endl;
        }
    });

    // 主线程阻塞等待
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 0;
}

优缺点分析

CPromise

  • 优点:代码简洁,易于理解;支持链式调用,使用方式类似前端 Promise;适合学习 Promise 原理。
  • 缺点:不支持内置异步操作,需自行结合线程;功能较为基础,缺乏高级特性。

std::promise

  • 优点:内置异步支持,与 std::future 配合功能强大;标准库实现,性能优化和稳定性有保障;兼容性好。
  • 缺点:使用方式相对复杂,缺乏链式调用支持;回调机制灵活性不如手写实现。

总结

通过手写 CPromise,我们可以深入理解 Promise 的核心机制,包括状态流转、回调队列管理等。在实际工程中,std::promise 依然是更稳健的选择,因为它提供了更强的功能和更好的性能保障。

对于开发者而言,理解手写实现的原理有助于更好地把握 std::promise 的工作方式,从而根据具体场景选择合适的工具来处理异步操作。

目录

  1. 前端 Promise 的应用与优势
  2. 常见应用场景
  3. 并发请求
  4. Promise 解决的问题
  5. 手写 C++ Promise 实现
  6. 类结构与成员变量
  7. 构造函数
  8. resolve 方法
  9. reject 方法
  10. then 方法
  11. onCatch 方法
  12. 链式调用与使用示例
  13. std::promise 与 CPromise 对比
  14. 基础功能对比
  15. 实现细节差异
  16. 代码示例对比
  17. 优缺点分析
  18. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 医疗 AI 可信系统:向量索引与贝叶斯网络实战
  • Midreal AI 工具深度解析:交互式小说生成与插图创作实践
  • C++ 二叉搜索树实现详解
  • Python 自学指南:培养良好编程习惯与避坑建议
  • C++ 性能优化实战:内存、CPU 与 I/O 效率提升指南
  • C++ 异常处理机制详解与避坑指南
  • 前端开发基础:HTML、CSS 与 JavaScript 入门
  • LeetCode 236 二叉树的最近公共祖先与 173 二叉搜索树迭代器
  • MySQL 8.0 Windows 安装配置教程
  • 大模型技术原理与应用指南:从入门到精通
  • 基于 SpringBoot 的留守儿童爱心管理系统
  • 深度可分离卷积基础原理与架构解析
  • Highcharts 矩形树图 Treemap 布局算法及层级交互实现
  • AI 大模型工程师职业前景与技术学习路径指南
  • HarmonyOS RcList 组件缩略图、角标与图标系统设计
  • AI 辅助开发实战:基于 DeepSeek 构建贪吃蛇游戏
  • 2024 年 AI 绘画现状:为何 MJ 与 SD 讨论热度变化
  • C++ 多态的概念、实现与原理详解
  • FRP 反向代理实现智能家居远程访问实战
  • 大模型应用开发:RAG 入门与实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online