Angular v1.6.5升级后,用Bluebird替换内置Promise调用$interval.cancel报错
问题背景
你在使用Angular搭配Bluebird和angular-bluebird-promises替换内置Promise后,升级到Angular v1.6.5时遇到了调用$interval.cancel()抛出Cannot set property 'pur' of undefined的异常。这个问题的根源其实是Angular新版本新增的代码和Bluebird的结构不兼容。
问题原因
Angular v1.6.5新增了两段用于标记Promise异常已处理的代码:
function markQStateExceptionHandled(state) { state.pur = true; } function markQExceptionHandled(q) { markQStateExceptionHandled(q.$$state); }
Angular的内置Promise对象带有$$state这个内部属性,但Bluebird的Promise并没有这个属性。当$interval.cancel()触发这段代码时,会尝试访问q.$$state得到undefined,再调用markQStateExceptionHandled就会抛出Cannot set property 'pur' of undefined的错误。
可行的解决方案
1. 给Bluebird Promise添加兼容的$$state属性
最简单的办法是在你的应用启动时,给Bluebird的Promise原型添加一个空的$$state对象,让Angular的代码能正常访问:
angular.module('yourAppModule').run(['$window', function($window) { // 为Bluebird Promise添加Angular依赖的$$state属性 Object.defineProperty($window.Promise.prototype, '$$state', { value: {}, writable: true, enumerable: false }); }]);
这个属性设为不可枚举,避免影响其他依赖Promise结构的代码,同时可写性确保Angular能给它设置pur属性。
2. 调整angular-bluebird-promises的适配逻辑
如果你能修改angular-bluebird-promises的源码,可以在它封装Bluebird的部分,主动给返回的Promise对象添加$$state属性,这样所有通过Angular服务获取的Promise都会自带这个兼容属性,不需要在应用代码里额外处理。
3. 临时降级Angular版本(不推荐长期使用)
如果暂时没时间处理兼容性代码,可以先降级到Angular v1.6.4或更早版本,这些版本还没有引入markQExceptionHandled相关的逻辑,不会触发这个异常。不过这只是权宜之计,还是建议尽早做兼容性适配。
验证方法
添加完兼容代码后,重新执行调用$interval.cancel()的逻辑,确认异常不再抛出。同时测试一下其他Promise相关的功能,比如$q的then/catch、异步操作的异常处理,确保不会引入新的问题。
内容的提问来源于stack exchange,提问作者Michal Dan

