@Domenic有一篇关于jQuery延迟对象的失败的非常透彻的文章:你错过了承诺的要点。在本文中,Domenic强调了jQuery承诺与其他承诺(包括Q、when.js、RSVP.js和ES6承诺)相比的一些失败之处
离开Domenic的文章时,我觉得jQuery承诺在概念上存在固有的缺陷。我试图举例说明这个概念
我认为jQuery实现有两个问题:
一,。.then方法不可链接
换句话说
承诺。然后(a)。然后(b)
当承诺履行时,jQuery将调用a,然后调用b
由于。然后在其他promise库中返回一个新的promise,因此它们的等价项是:
承诺。然后(a)
承诺。然后(b)
二,。异常处理在jQuery中冒泡
另一个问题似乎是异常处理,即:
试试看{
承诺。然后(a)
}捕获(e){
}
Q中的等效值为:
试试看{
承诺。然后(a)完成
}捕获(e){
//.done()重新抛出来自
}
在jQuery中,当a未能捕获块时,异常抛出并冒泡。在另一种情况下,a中的任何异常都将被传递到.done或.catch或其他异步catch。如果promise API调用均未捕获异常,则异常将消失(因此,Q最佳做法是使用.done释放任何未处理的异常)
 
上面的问题是否涉及jQuery实现承诺的问题,或者我是否误解或忽略了这些问题
编辑这个问题与jQuery有关<;3.0; 从jQuery 3.0开始,alpha jQuery符合承诺/A+标准
更新:jQuery3.0修复了下面列出的问题。这是真正的承诺/A+合规
是的,jQuery承诺存在严重的固有问题
也就是说,由于这篇文章是jQuery写的,因此jQuery做出了巨大的努力来解决更多的Promises/Aplus问题,现在他们有了一个.then方法来解决这个问题
因此,即使在jQueryreturnsPromise().then(a).then(b)中,承诺返回函数a和b也将按预期工作,在继续前进之前先展开返回值。如这把小提琴所示:
函数超时(){
var d=$.Deferred();
setTimeout(函数(){d.resolve();},1000);
返回d.promise();
}
超时()。然后(函数(){
document.body.innerHTML=“第一”;
返回超时();
}).然后(函数(){
document.body.innerHTML+=“<;br/>;秒”;
返回超时();
}).然后(函数(){
document.body.innerHTML+=“<;br/>;第三”;
返回超时();
});
然而,jQuery的两个巨大问题是错误处理和意外的执行顺序
错误处理
与catch不同,即使您解决了被拒绝的jQuery承诺,也无法将其标记为“已处理”。这使得jQuery中的拒绝本质上被破坏,并且非常难以使用,与同步try/catch不同
你能猜出这里有什么日志吗?(小提琴)
超时()。然后(函数(){
抛出新错误(“Boo”);
}).然后(函数(){
log(“你好世界”);
},函数(){
log(“在错误处理程序中”);
}).然后(函数(){
log(“本应已运行”);
}).fail(函数(){
log(“但它会这样做”);
});
如果您猜到“未捕获错误:boo”您是正确的。jQuery承诺是不安全的。与Promises/Aplus Promises不同,它们不会让您处理任何抛出的错误。那么拒绝安全性呢?(小提琴)
超时()。然后(函数(){
var d=$.Deferred();d.reject();
返回d;
}).然后(函数(){
log(“你好世界”);
},函数(){
log(“在错误处理程序中”);
}).然后(函数(){
log(“本应已运行”);
}).fail(函数(){
log(“但它会这样做”);
});
下面的日志“在错误处理程序”中,但实际上是“-根本无法处理jQuery承诺拒绝。这与您预期的流程不同:
试试看{
抛出新错误(“Hello World”);
}捕获(e){
log(“在错误处理程序中”);
}
log(“本应已运行”);
这是您通过诸如Bluebird和Q之类的Promises/A+库获得的流程,以及您期望的有用性。这是巨大的,投掷安全是承诺的一大卖点。这是蓝鸟在这种情况下的正确动作
执行令
如果基础承诺已经解析,jQuery将立即执行传递的函数,而不是延迟它,因此代码的行为将根据我们附加处理程序的承诺是否已经解析而有所不同。这将有效地释放Zalgo,并可能导致一些最痛苦的错误。这会产生一些最难调试的bug
如果我们看下面的代码:(fiddle)
函数超时(){
var d=$.Deferred();
setTimeout(函数(){d.resolve();},1000);
返回d.promise();
}
控制台日志(“本”);
var p=超时();
p、 然后(函数(){
log(“预期来自异步api”);
});
控制台日志(“is”);
setTimeout(函数(){
控制台日志(“He”);
p、 然后(函数(){
控制台.log(“————————————————————————;
});
console.log(“Comes”);
},2000);
我们可以观察到非常危险的行为,setTimeout等待原始超时结束,因此jQuery切换其执行顺序,因为。。。谁喜欢不会导致堆栈溢出的确定性API?这就是Promissions/A+规范要求承诺总是推迟到事件循环的下一次执行的原因
旁注
值得一提的是,像Bluebird这样更新、更强大的promise库(以及实验性的时候)不需要像Q那样在链的末尾使用.done,因为它们能够自己发现未处理的拒绝,因此它们也比jQuery promises或Q promises快得多