引言
在前端开发中,Promise 是处理异步操作的重要工具。它通过将异步操作封装在 Promise 实例中,解决了传统回调地狱的问题,提高了代码的可读性和可维护性。Promise 的概念并非前端独有,在 C++11 标准中也引入了 ,用于实现类似的功能。
基于 C++11 标准手写了一个类似前端 Promise 的类 CProimse,实现了状态管理、回调注册及链式调用功能。通过与 C++ 标准库中的 std::promise 进行对比,分析了两者在基础功能、实现细节(状态管理、回调机制、异步支持)及优缺点上的差异。手写实现有助于理解 Promise 原理,而 std::promise 则提供了更强大的内置异步支持和稳定性保障,开发者应根据实际需求选择合适的工具处理异步操作。

在前端开发中,Promise 是处理异步操作的重要工具。它通过将异步操作封装在 Promise 实例中,解决了传统回调地狱的问题,提高了代码的可读性和可维护性。Promise 的概念并非前端独有,在 C++11 标准中也引入了 ,用于实现类似的功能。
std::promise本文将从一个手写的 C++ Promise 实现(基于 C++11)出发,分析其工作原理,并与 std::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);
});
catch 方法可以集中处理所有异步操作中的错误。Promise.all 和 Promise.race,可以方便地控制多个异步操作的执行顺序和结果。template<typename Element>
class CProimse {
private:
using Resolve = std::function<void(Element)>;
using Reject = std::function<void(const std::string&)>;
private:
Element m_element; // 异步操作的结果
std::string m_reason; // 拒绝的原因
CProimseState m_state; // 当前状态
std::list<Resolve> m_resolves; // 成功回调函数列表
std::list<Reject> m_rejects; // 失败回调函数列表
public:
CProimse();
void reject(const std::string& reason);
void resolve(Element element);
void onCatch(const Reject& rej);
CProimse* then(const Resolve& res);
};
Resolve 和 Reject:定义了成功和失败回调函数的类型。m_element 和 m_reason:分别存储 Promise 的结果和拒绝原因。m_state:表示 Promise 的当前状态,初始状态为 PENDING。m_resolves 和 m_rejects:存储注册的成功和失败回调函数列表。CProimse() : m_state(CProimseState::PENDING) {}
void resolve(Element element) {
m_element = element;
if (m_state == CProimseState::PENDING) {
m_state = CProimseState::FULFILLED;
for (Resolve res : m_resolves) {
res(element);
}
}
}
void reject(const std::string& reason) {
m_reason = reason;
if (m_state == CProimseState::PENDING) {
m_state = CProimseState::REJECTED;
for (Reject rej : m_rejects) {
rej(reason);
}
}
}
CProimse* then(const Resolve& res) {
if (m_state == CProimseState::FULFILLED) {
res(m_element);
} else if (m_state == CProimseState::PENDING) {
m_resolves.push_back(res);
}
return this;
}
void onCatch(const Reject& rej) {
if (m_state == CProimseState::REJECTED) {
rej(m_reason);
} else if (m_state == CProimseState::PENDING) {
m_rejects.push_back(rej);
}
}
通过 then 和 onCatch 方法,可以实现链式调用,使得异步操作的处理更加简洁和直观。
proimse->then([](int ele) -> void {
std::cout << ele << std::endl;
})->onCatch([](const std::string& reason) -> void {
std::cout << reason << std::endl;
});
CProimse<int>* proimse = new CProimse<int>();
proimse->then([](int ele) -> void {
std::cout << ele << std::endl;
})->onCatch([](const std::string& reason) -> void {
std::cout << reason << std::endl;
});
proimse->reject("网络异常!!!");
std::promise 与 CProimse 对比| 功能 | CProimse 实现 | std::promise |
|---|---|---|
| 状态管理 | 手动实现 | 标准库实现 |
| 回调注册与执行 | 手动实现 | 标准库实现 |
| 异步支持 | 需结合线程 | 内置支持 |
| 链式调用 | 支持 | 不支持 |
CProimse:通过自定义枚举 CProimseState 管理状态。std::promise:状态管理由标准库实现,用户无需关注底层细节。CProimse:手动维护回调队列,通过 then 和 onCatch 方法注册回调。std::promise:通过 std::future 与 std::promise 配合,回调通过 future 的 get 方法触发。CProimse:需要结合 std::thread 或其他异步框架实现异步操作。std::promise:内置支持异步操作,通常与 std::async 或 std::thread 结合使用。CProimse:支持链式调用,通过返回 this 实现。std::promise:不支持链式调用,无法直接链式注册回调。CProimse 示例CProimse<int>* proimse = new CProimse<int>();
proimse->then([](int ele) -> void {
std::cout << ele << std::endl;
})->onCatch([](const std::string& reason) -> void {
std::cout << reason << std::endl;
});
proimse->reject("网络异常!!!");
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();
// 等待结果并获取
int result = fut.get();
std::cout << "结果:" << result << std::endl;
// 主线程阻塞等待
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
CProimsestd::promise 的高级特性。std::promisestd::future 配合使用,功能强大。通过手写 CProimse,我们可以深入理解 Promise 的实现原理,包括状态管理、回调注册与执行等核心机制。然而,在实际开发中,std::promise 仍然是更好的选择,因为它提供了更强大的功能和更好的性能保障。
对于开发者来说,理解 std::promise 的工作原理以及其与手写实现的异同点,有助于更好地选择合适的工具来处理异步操作。同时,手写实现虽然功能有限,但作为学习和探索的工具,仍然具有重要的价值。
希望本文能够帮助读者更好地理解 Promise 的实现原理,并在实际开发中做出更明智的选择。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online