基于Proxy实现可更新Promise的技术问询与问题验证
可更新Promise实现相关问题
编辑说明
已更新本问题,旧问题已迁移。可通过代码片段查看POC。
目标
实现或模拟可更新、生命周期可重置的Promise,同时通过控制反转实现对Promise决议的更强控制,示例代码如下:
const ex = {}; const prom = new Promise<T>((resolve, reject) => { ex.resolve = resolve; ex.reject = reject; });
实现方案
代码片段支持输入新值并选择决议状态(resolve/reject),被拒绝的值将高亮显示。默认情况下,Promise以值default被拒绝。
编辑补充:默认已绑定
.then()回调(代码第85行),首次决议后值会被打印两次,此为预期行为。
核心代码
JavaScript实现
class UpdatablePromise { /** * @param prom - 初始Promise对象 * @param executor - 可选参数,用于将执行器存储到内部 * @returns UpdatablePromise实例 */ constructor(prom, executor) { this.value = undefined; this.executors = []; if (executor) this.executors.push(Object.assign({}, executor)); /** * 调用之前存储的resolve/reject函数,完成旧Promise的决议 * 辅助函数,减少update方法中的代码重复 */ const prevResolvers = (type) => { return (val) => { const flag = type === "reject"; while (true) { const executor = this.executors.shift(); if (executor) flag ? executor.reject(val) : executor.resolve(val); else break; } return val; }; }; const handler = { get: (target, prop) => { if (prop === "update") { // 返回更新方法,接收新Promise和可选执行器 return (v, executor) => { this.value = v; if (executor) this.executors.push(executor); // 绑定新Promise的回调,在其决议时完成所有旧Promise的决议 v.then( (val) => prevResolvers("resolve")(val), (val) => prevResolvers("reject")(val) ); }; } else if (typeof this.value === "undefined") { // 若未更新过,返回初始Promise的属性/方法 return typeof target[prop] === "function" ? target[prop].bind(target) : target[prop]; } else if (prop === "then") { // 转发then方法到当前最新的Promise return (onfulfilled, onrejected) => this.value.then(onfulfilled, onrejected); } else if (prop === "catch") { return (onrejected) => this.value.catch(onrejected); } else if (prop === "finally") { return (onfinally) => this.value.finally(onfinally); } else { return target[prop]; } }, }; return new Proxy(prom, handler); } }
配套HTML页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <label for="input">输入更新Promise的值:</label> <input id="input" /> <div> <button id="res">resolve</button> <button id="rej">reject</button> </div> <pre id="output"></pre> </body> </html>
此外还有TypeScript版本(可能未同步更新,优先参考上述JS代码)。
问题背景
该实现在控制台或在线编辑器中可正常运行,最初误以为在Workbox的Service Worker中仅返回旧值,后排查发现是其他函数反复将Promise更新至数据库值导致,实际在Service Worker中可用。
核心问询
- 该实现是否真如预期工作?即除可重置生命周期外,完全兼容Promise特性,更新后所有待处理的
await或回调均使用新值决议。 - 若实现有效,能否添加
state成员变量以同步追踪Promise状态?
解答
1. 实现的有效性与兼容性
该实现基本符合预期,但有几个细节需要注意:
- 待处理回调/await的触发:调用
update传入新Promise后,新Promise决议时会遍历执行所有存储的旧executor的resolve/reject,能让所有之前挂起的await或.then()回调拿到新值,符合设计目标。 - Promise兼容性:通过代理拦截
then/catch/finally并转发到最新Promise,保证了大部分原生Promise特性的兼容。但存在小局限:- 原生Promise的内部属性(如
Symbol.toStringTag)未被代理,若业务代码依赖这些属性可能出现问题; - 多次调用
update时,若前一个Promise未决议,新Promise决议会覆盖等待队列,这符合“重置生命周期”的设计,但需确认业务场景是否接受; - 已决议的Promise回调不会重复触发,符合原生Promise特性。
- 原生Promise的内部属性(如
整体来看,在常规业务场景下,该实现能满足“可更新、兼容Promise特性”的需求。
2. 添加state成员变量追踪状态
可以添加state变量同步追踪当前Promise的状态,实现思路如下:
- 在类初始化时添加状态变量:
constructor(prom, executor) { this.value = undefined; this.executors = []; this.state = 'pending'; // 初始状态为pending // ... 其他原有代码 } - 在
update方法中重置状态,并监听新Promise的决议更新状态:return (v, executor) => { this.value = v; this.state = 'pending'; // 新Promise初始为pending if (executor) this.executors.push(executor); v.then( (val) => { this.state = 'fulfilled'; return prevResolvers("resolve")(val); }, (val) => { this.state = 'rejected'; return prevResolvers("reject")(val); } ); }; - 在代理的
get方法中,当访问state时返回当前值:const handler = { get: (target, prop) => { if (prop === 'state') { return this.state; } // ... 其他原有逻辑 } };
通过上述修改,就能实时同步追踪当前活跃Promise的状态(pending/fulfilled/rejected)。
内容的提问来源于stack exchange,提问作者UmbQbify
相关产品推荐
相关产品推荐

