链接两个异步 jQuery 函数时如何完全避开 jQuery 承诺?

Posted

技术标签:

【中文标题】链接两个异步 jQuery 函数时如何完全避开 jQuery 承诺?【英文标题】:How to dodge jQuery promises completely when chaining two async jQuery functions? 【发布时间】:2015-09-28 02:22:35 【问题描述】:

我看过很多关于新的 EMCA 承诺的教程,它们提倡避免 jQuery 库中的“承诺”。他们通常说你可以通过这样做来躲避他们:

Promise.resolve($.getJSON(url, params)); // voila!  the jQuery promise is "gone"!

但是,当我必须将两个异步 jQuery 函数链接在一起时,这实际上不起作用。如何在不使用 jQuery 的 then() 或 .when() 的情况下将两个 getJSON 调用(第二个调用取决于第一个)链接在一起?

相反,我只想使用 Promise.all 等。

我认为一个类似的问题是交错 jquery 和 EMCA 承诺?

【问题讨论】:

【参考方案1】:

javascript 承诺是 interoperable。您可以随意混合它们,所有适当的库1 和本机承诺都接受来自任何实现anywhere3 的 thenables2支持>。如果出现外来的东西,他们只会在上面做Promise.resolve

因此,通常您会编写代码,就好像它们都使用相同的 Promise 实现一样,并且它可以正常工作。 但是,现在您要确保所有.then 方法调用都使用您最喜欢的实现;或者您想使用非标准方法或功能?为此,您必须明确强制转换所有承诺,您将在其上直接调用方法 - 仅此而已。

一些例子:

Promise.all([$.ajax(…), $.ajax(…)]).then(…); // just works!
$.ajax(…) // a jQuery promise
.then(…)  // so this would be jQuery `then`, which we don't want.
Promise.resolve($.ajax(…)) // explicit cast
.then(function(data)      // native `then`
    return $.ajax(…);      //   just works!
)                         // returns a native promise still
.catch(…)                  // so we can use its features

1:是的,jQuery 直到 3.0 版才属于其中之一 2:虽然所有 jQuery 的延迟和承诺都是这样的thenables 3:真的无处不在你会期待一个承诺,在Promise.resolvethen回调返回值,Promise.all参数,......

【讨论】:

请注意,Promises/A+ 的目标之一是与 jQuery Promise 兼容,然后您可以将 jQuery “promises”返回给 Promises/A+,它会正确使用他们。正如您指出的那样,另一个方向仅适用于 3.0 。说从then 链返回一个值,或将值传递给像.all 这样的聚合方法可能是一个隐式Promise.resolve..【参考方案2】:

您可以采用两种方法中的任何一种...

转换然后合并:

var p1 = Promise.resolve($.getJSON(url_1, params_1)); // voila 1!
var p2 = Promise.resolve($.getJSON(url_2, params_2)); // voila 2!
var p3 = Promise.all([p1, p2]).then(...);

合并然后转换:

var p1 = $.getJSON(url_1, params_1);
var p2 = $.getJSON(url_2, params_2);
var p3 = Promise.resolve($.when(p1, p2)).then(...); // voila 1 and 2!

直截了当,任何一种方法都会给你一个原生的 ES6 承诺,p3,它在两个 jQuery 承诺解决时解决,或者在任何一个承诺失败时被拒绝。

但是,您可能对两次getJSON() 调用的结果感兴趣,而jQuery 在这方面很尴尬。 jQuery 的 jqXHR 承诺将 多个 参数传递给它们的成功和错误回调,而 ES6 承诺将只接受一个;其余的将 被忽视。幸运的是,将多个参数捆绑在一起以创建一个对象是相当简单的。这必须在转换为 ES6 之前在 jQuery 中完成。

“convert then combine”代码展开如下:

var p1 = Promise.resolve($.getJSON(url_1, params_1).then(
    function(data, textStatus, jqXHR) 
        return  data:data, textStatus:textStatus, jqXHR:jqXHR ;
    ,
    function(jqXHR, textStatus, errorThrown) 
        return  jqXHR:jqXHR, textStatus:textStatus, errorThrown:errorThrown ;
    
));
var p2 = Promise.resolve($.getJSON(url_2, params_2).then(
    function(data, textStatus, jqXHR) 
        return  data:data, textStatus:textStatus, jqXHR:jqXHR ;
    ,
    function(jqXHR, textStatus, errorThrown) 
        return  errorThrown:errorThrown, textStatus:textStatus, jqXHR:jqXHR ;
    
));
var p3 = Promise.all([p1, p2]).then(
    function(results) 
        // results[0] will be an object with properties .data, .textStatus, .jqXHR 
        // results[1] will be an object with properties .data, .textStatus, .jqXHR 
    ,
    function(rejectVal) 
        // rejectVal will be an object with properties .errorThrown, .textStatus, .jqXHR
    
);

“组合然后转换”的方法稍微有点棘手,因为组合结果(在 jQuery 中)显示为 arguments 列表,它本身需要转换(仍然在 jQuery 中)为数组。

var p1 = $.getJSON(url_1, params_1).then(
    function(data, textStatus, jqXHR)  
        return  data:data, textStatus:textStatus, jqXHR:jqXHR ; 
    ,
    function(jqXHR, textStatus, errorThrown)  
        return  errorThrown:errorThrown, textStatus:textStatus, jqXHR:jqXHR ; 
    
);
var p2 = $.getJSON(url_2, params_2).then(
    function(data, textStatus, jqXHR)  
        return  data:data, textStatus:textStatus, jqXHR:jqXHR ;
    ,
    function(jqXHR, textStatus, errorThrown)  
        return  errorThrown:errorThrown, textStatus:textStatus, jqXHR:jqXHR ; 
    
);
var p3 = Promise.resolve($.when(p1, p2).then(function() 
    return [].slice.call(arguments);// <<< convert arguments list to Array
)).then(
    function(results)  
        // results[0] will be an object with properties .data, .textStatus, .jqXHR
        // results[1] will be an object with properties .data, .textStatus, .jqXHR
    ,
    function(rejectVal)  
        // rejectVal will be an object with properties .errorThrown, .textStatus, .jqXHR
    
);

DEMO: resolved

DEMO: rejected

【讨论】:

干得好!我只需要说我多么欣赏这个答案。我读过几篇文章提到将 jQuery.ajax() 调用转换为原生 Promises,但没有提到在 ajax 完成时捕获传递给回调方法的所有参数。 谢谢。事后看来,jQuery 的人应该从一开始就捆绑这样的参数。他们 10 年前的开拓精神给他们留下了一些令人讨厌的小遗留问题。 这是一个很好的答案,谢谢!也就是说,在 jQuery 3.0 中,jQuery.Deferred is now Promises/A+ compatible. @Avraham,感谢您的称赞。您是否建议 Promises/A+ 兼容性应该改变我的答案? @Avraham,我认为不需要第三个示例。 jQuery 3 的 A+ 合规性确实让 jQuery 加入了 A+ 俱乐部,但这个问题完全是关于 jQuery 承诺的同化,这总是可能的。小提琴中的代码将在 jQuery 1 和 2 以及 3 下运行。

以上是关于链接两个异步 jQuery 函数时如何完全避开 jQuery 承诺?的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS 在异步函数(async/await)中调用栈打印不全的问题

如何使用jquery取出json格式的数据

jQuery异步框架探究3:jQuery.when方法

jQuery异步框架探究3:jQuery.when方法

页面重定向后的Jquery函数?

jQuery 回调函数和方法链接使用