题目
实现一个可取消的延迟Promise
信息
- 类型:问答
- 难度:⭐⭐
考点
Promise封装, 异步控制, 取消机制设计, ES6+特性应用
快速回答
实现要点:
- 创建返回
{ promise, cancel }对象的函数 - 使用
setTimeout实现延迟逻辑 - 在Promise内部保存
reject方法供外部调用 - 取消时使用特定错误对象标识取消状态
- 添加状态保护防止重复执行
原理说明
原生Promise不支持取消操作,需利用闭包特性保存reject函数。核心设计:
- 创建pending状态的Promise
- 通过闭包暴露reject方法作为取消触发器
- 添加状态锁防止多次resolve/reject
- 使用特定错误类型区分正常错误和取消操作
代码示例
function createCancelableDelay(duration) {
let cancelHandler = null;
let isCancelled = false;
const promise = new Promise((resolve, reject) => {
cancelHandler = reject; // 保存reject引用
setTimeout(() => {
if (!isCancelled) {
resolve(`Completed after ${duration}ms`);
}
}, duration);
});
const cancel = (reason = 'DELAY_CANCELLED') => {
if (!isCancelled) {
isCancelled = true;
cancelHandler(new Error(reason)); // 触发拒绝
}
};
return { promise, cancel };
}
// 使用示例
const { promise, cancel } = createCancelableDelay(2000);
promise
.then(console.log)
.catch(err => {
if (err.message === 'DELAY_CANCELLED') {
console.log('Delay was cancelled');
} else {
console.error('Real error:', err);
}
});
// 在1秒后取消
document.getElementById('cancelBtn').addEventListener('click', cancel);最佳实践
- 错误区分:使用自定义错误类型(如
CancelError)替代原始Error - 状态清理:取消后清除定时器避免内存泄漏
- API设计:返回对象包含promise和cancel方法,符合人体工学
- Typescript支持:添加类型声明增强安全性
常见错误
| 错误类型 | 现象 | 解决方案 |
|---|---|---|
| 未处理取消拒绝 | 导致UnhandledPromiseRejection | 必须添加catch处理 |
| 多次取消调用 | 重复触发reject | 添加状态锁(isCancelled) |
| 未清除定时器 | 内存泄漏 | 在cancel中调用clearTimeout |
扩展知识
- AbortController:浏览器原生取消方案,需配合fetch API使用
- RxJS Observable:响应式编程库提供完善取消机制
- Promise.race():可通过竞争实现超时取消(需额外Promise)
- React useEffect:在组件卸载时自动取消异步操作的实际应用场景