为何在Promise中使用defer?代码优势及替代方案咨询
首先得澄清一个关键点:MDN提到的废弃的Promise.defer()是早期Promise规范里的内置方法,而你代码里自己实现的defer()函数是一种手动封装的"延迟Promise"模式,不属于废弃的API范畴——这是很多人容易混淆的点。
接下来逐个解答你的问题:
1. 为什么要在Promise中使用这种defer模式?
这种模式的核心作用是把Promise的resolve和reject方法暴露到Promise构造函数外部,方便在其他异步逻辑(比如事件监听、定时轮询、外部回调)中触发Promise的状态变更。
比如你提供的waitForPredicate函数,它可能需要持续监听某个数据变化,直到predicate(data)返回true,这时候用defer就可以在监听回调里直接调用deferredFetcher.resolve(result),不用把整个监听逻辑都塞进Promise的构造函数里。
2. 用defer的优势仅为减少代码量吗?
不完全是。减少重复代码是一个好处(不用每次都写Promise构造函数并手动挂载resolve/reject),但更重要的是逻辑分离:
- Promise的创建和状态触发逻辑可以拆分开,在复杂的异步流程中(比如多阶段的异步操作、外部事件驱动的场景),代码结构会更清晰。
- 避免把大量业务逻辑堆在Promise构造函数的回调里,让代码更易读和维护。
不过这种优势是有场景限制的,在简单的异步流程里,它的优势并不明显。
3. 是否应该直接定义新Promise来替代它?
非常推荐用标准的new Promise写法替代,原因有这些:
- 更符合Promise的设计理念:Promise的构造函数本身就是用来封装异步操作的,把resolve/reject的触发逻辑放在构造函数里,代码的意图更明确,其他开发者一眼就能看懂什么时候Promise会被resolve/reject。
- 避免额外的抽象层:自定义的
defer()函数会增加一层封装,对于不熟悉这个模式的开发者来说,需要额外理解这个函数的作用,反而增加了认知成本。
举个例子,把你的waitForPredicate用标准Promise改写的大致结构:
export async function waitForPredicate(peer, path, predicate, cancelToken) { return new Promise((resolve, reject) => { // 这里写监听或轮询逻辑 const checkData = () => { const data = /* 获取数据的逻辑 */ let result = predicate(data); if (result) { resolve(result); } // 处理取消逻辑或继续监听 if (cancelToken?.isCancelled) { reject(new Error("Operation cancelled")); } }; // 启动监听/轮询 checkData(); // 比如绑定事件监听 peer.on("dataUpdate", checkData); }); }
总结
你自己实现的defer()模式本身没有问题,但在现代Promise开发中,标准的new Promise写法更通用、更直观,也更容易被团队其他成员理解。如果你的开发指导要求使用它,建议和对方沟通一下这个模式的适用场景,以及标准写法的优势,权衡之后再做选择。
内容的提问来源于stack exchange,提问作者Rasim AVCI

