链接两个异步 jQuery 函数时如何完全躲避 jQuery Promise?

IT技术 javascript jquery promise es6-promise
2021-01-15 21:53:56

我已经看到许多关于新 EMCA Promise的教程都提倡避免 jQuery 库中的“Promise”。他们通常说你可以通过做这样的事情来躲避他们:

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

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

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

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

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 Promise,p3当 jQuery Promise都解决时解决,或者当任何一个Promise失败时被拒绝。

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

“转换然后组合”代码扩展如下:

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
    }
);

演示:已解决

演示:拒绝

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

JavaScript Promise是可互操作的您可以随意混合它们,所有适当的库1和本机Promise都接受来自任何地方的任何实现的thenables 2 3如果出现外国的东西,他们就会去做Promise.resolve

所以通常你会写你的代码,就好像它们都使用相同的Promise实现一样,它只是有效
但是,现在您要确保所有.then方法调用都使用您喜欢的实现;或者您想使用非标准方法或功能?对于这一点,你必须明确地投下所有的Promise在其上直接调用方法-而不是其他。

一些例子:

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 延迟和Promise都是如此thenables,尽管
3:真的无处不在,您期望Promise,在Promise.resolvethen回调返回值,Promise.all参数,......

请注意,Promises/A+目标之一是与 jQuery Promise兼容,您可以将 jQuery“Promise”返回给 Promises/A+,然后它会正确使用它们。正如您指出的那样,另一个方向仅适用于 3.0 。可以说从then中返回值或将值传递给聚合方法(例如.all等等)是隐式的Promise.resolve..
2021-03-27 21:53:56