jQuery $ .Deferred(jQuery 1.x/2.x)固有的问题

Bri*_*unt 45 jquery promise jquery-deferred q es6-promise

@Domenic有一篇关于jQuery延迟对象失败的非常详尽的文章:你错过了Point of Promises.在其中,Domenic突出了jQuery承诺的一些失败,与其他包括Q,when.js,RSVP.js和ES6承诺相比.

我离开了Domenic的文章,认为jQuery承诺在概念上有一个固有的失败.我试图把这个例子放在这个概念上.

我认为jQuery实现有两个问题:

1.该.then方法不可链接

换一种说法

promise.then(a).then(b)
Run Code Online (Sandbox Code Playgroud)

jQuery将调用a随后b的时候promise满足.

由于.then在其他promise库中返回一个新的promise,它们的等价物将是:

promise.then(a)
promise.then(b)
Run Code Online (Sandbox Code Playgroud)

2.异常处理在jQuery中冒泡.

另一个问题似乎是异常处理,即:

try {
  promise.then(a)
} catch (e) {
}
Run Code Online (Sandbox Code Playgroud)

Q中的等价物是:

try {
  promise.then(a).done()
} catch (e) {
   // .done() re-throws any exceptions from a
}
Run Code Online (Sandbox Code Playgroud)

在jQuery中,当acatch块失败时异常抛出并出现气泡.在其他承诺中,任何异常a将被传递到.done.catch或其他异步捕获.如果没有任何promise API调用捕获异常,它就会消失(因此Q最佳实践,例如使用.done释放任何未处理的异常).

 

上述问题是否涵盖了jQuery实现承诺的问题,还是我误解或遗漏了问题?


编辑 此问题与jQuery <3.0; 从 jQuery 3.0开始,alpha jQuery是Promises/A +兼容的.

Ben*_*aum 52

更新:jQuery 3.0修复了下面列出的问题.这是真正的Promises/A +兼容.

是的,jQuery承诺存在严重且固有的问题.

也就是说,自从撰写这篇文章以来,jQuery做出了很大的努力,成为更多的Promises/Aplus投诉,他们现在有了一个链接的.then方法.

因此,即使在jQuery returnsPromise().then(a).then(b)for promise返回函数a并且b将按预期工作,在继续前进之前展开返回值.正如这个小提琴所示:

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}

timeout().then(function(){
   document.body.innerHTML = "First";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Second";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Third";
   return timeout();
});
Run Code Online (Sandbox Code Playgroud)

但是,jQuery 的两个巨大问题是错误处理和意外的执行顺序.

错误处理

没有办法将jQuery承诺标记为"已处理",即使你解决了它,与catch不同.这使得jQuery中的拒绝固有地破坏并且非常难以使用,就像同步一样try/catch.

你能猜出这里有什么记录吗?(小提琴)

timeout().then(function(){
   throw new Error("Boo");
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});
Run Code Online (Sandbox Code Playgroud)

如果你猜对了"uncaught Error: boo"你是对的.jQuery promises 不是安全的.与Promises/Aplus承诺不同,他们不会让你处理任何抛出的错误.拒绝安全怎么办?(小提琴)

timeout().then(function(){
   var d = $.Deferred(); d.reject();
   return d;
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});
Run Code Online (Sandbox Code Playgroud)

以下日志"In Error Handler" "But this does instead"- 根本没有办法处理jQuery promise拒绝.这与您期望的流程不同:

try{
   throw new Error("Hello World");
} catch(e){
   console.log("In Error handler");
}
console.log("This should have run");
Run Code Online (Sandbox Code Playgroud)

使用像Bluebird和Q这样的Promises/A +库,以及您对实用性的期望是什么.这是巨大的,投掷安全是承诺的一大卖点.这里是Bluebird在这种情况下,正确的行动.

执行顺序

如果底层的promise已经解决,jQuery将立即执行传递的函数而不是推迟它,因此代码的行为会有所不同,具体取决于我们是否将处理程序附加到已拒绝的处理程序已经解决.这有效地释放了Zalgo并且可能导致一些最痛苦的错误.这创建了一些最难调试的bug.

如果我们看下面的代码:( 小提琴)

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}
console.log("This");
var p = timeout();
p.then(function(){
   console.log("expected from an async api.");
});
console.log("is");

setTimeout(function(){
    console.log("He");
    p.then(function(){
        console.log("?????Z???????A?????L????????G???????O???!????? *");
    });
    console.log("Comes");
},2000);
Run Code Online (Sandbox Code Playgroud)

我们可以观察到这么危险的行为,setTimeout等待原始超时结束,所以jQuery切换它的执行顺序,因为...谁喜欢不会导致堆栈溢出的确定性API?这就是为什么Promises/A +规范要求promises总是延迟到事件循环的下一次执行.

边注

值得一提的是像Bluebird这样更新更强大的承诺库(以及实验性的When)并不.done像Q那样需要在链的末端,因为他们自己找出了未处理的拒绝,它们也比jQuery承诺或Q承诺快得多.

  • 显然Bluebird的@Esailija指出有一个黑暗的伏都教技巧 - 从jQuery返回一个履行的承诺(!).然后错误处理程序会将承诺标记为已处理. (8认同)
  • 这不是一个黑暗的伏都教技巧,也没有必要去寻找源头来发现它.自从jQuery 1.8发布以来,文档已经读过"这些过滤器函数可以返回一个新值,传递给promise的.done()或.fail()回调,或者它们可以返回另一个可观察对象(Deferred,Promise,等)将其解析/拒绝的状态和值传递给promise的回调"`.因此,不是一个"巨大的问题",如果你活着,实际上根本不是问题. (6认同)