假设您的网站上有一个功能,该功能仅在 5% 的时间内使用。 该功能需要一些 HTML、CSS 和 JavaScript 代码才能运行。 因此,您决定不将这些 HTML、CSS 和 JavaScript 代码直接放在页面上,而是在使用该功能时通过 Ajax 加载它们。
我们需要发出三个 Ajax 请求。 由于我们不想在功能准备好之前向用户显示任何内容(此外,它们都某种程度上依赖于彼此才能正常工作),因此我们需要等待所有三个请求完成后才能继续。
最好的方法是什么?
jQuery 中的 Ajax 调用提供回调函数
$.ajax({
statusCode: {
url: "/feature",
success: function() {
// Ajax success
}
}
});
或者“Deferred”方式,这次使用简写 $.get()
方法
$.get("/feature/").done(function() {
// Ajax success
});
但是我们需要执行三个 Ajax 请求,并且希望在执行任何操作之前等待所有三个请求完成,因此在回调函数领域可能会变得非常复杂
// Get the HTML
$.get("/feature/", function(html) {
// Get the CSS
$.get("/assets/feature.css", function(css) {
// Get the JavaScript
$.getScript("/assets/feature.js", function() {
// All is ready now, so...
// Add CSS to page
$("<style />").html(css).appendTo("head");
// Add HTML to page
$("body").append(html);
});
});
});
这成功地等待所有内容都准备就绪,然后才将任何内容添加到页面中。 因此,当用户看到任何内容时,它就可以使用了。 也许这会让一些人感到反胃,但我之前也做过这样的事情。 至少它是有意义的并且有效。 **问题是什么?** 它很慢。
一个请求……等待完成……另一个请求……等待完成……另一个请求……等待完成……开始。
如果我们可以这样做会更快
所有三个请求同时进行……等待所有三个请求完成……开始。
我们可以使用一些 Deferred/Promises 操作来帮助解决这个问题。 我相信这对你们中的一些人来说是 JavaScript 101 的内容,但这种事情困扰了我很久,更复杂的 Promises 内容仍然如此。
在我们简单的用例中,我们可以使用 jQuery 的 $.when()
方法,它接受这些“Deferred”对象的列表(所有 jQuery Ajax 方法都返回 Deferred 对象),然后提供单个回调函数。
$.when(
// Deferred object (probably Ajax request),
// Deferred object (probably Ajax request),
// Deferred object (probably Ajax request)
).then(function() {
// All have been resolved (or rejected), do your thing
});
因此,我们的回调地狱可以重写为
$.when(
// Get the HTML
$.get("/feature/", function(html) {
globalStore.html = html;
}),
// Get the CSS
$.get("/assets/feature.css", function(css) {
globalStore.css = css;
}),
// Get the JS
$.getScript("/assets/feature.js")
).then(function() {
// All is ready now, so...
// Add CSS to page
$("<style />").html(globalStore.css).appendTo("head");
// Add HTML to page
$("body").append(globalStore.html);
});
另一个用例:芥末切割
我上面提供的用例示例是一个 5% 的功能。 为 95% 不使用该功能的用户减轻页面负担,并使其成为使用该功能的用户相对快速的附加功能。
另一种情况可能是“达到标准”的情况,在这种情况下,您可以根据需要在某些情况下向页面添加其他功能或内容。 也许对一些媒体查询执行 matchMedia
测试,并确定设备的屏幕和功能是否能够包含一些额外的模块。 太好了,使用一些并行 Ajax 调用来实现吧!
这是我见过的关于 jQuery Deferred 最简单的解释。 非常好! 谢谢 Chris!
嘿 Chris,感谢这篇文章。
为什么您更喜欢将获取的结果放在全局作用域中?
也可以正常工作。 :)
这可能更好,并且对于转换的人来说可能更舒服。 我尝试过类似的方法,但无法使其正常工作,但我确信我只是弄错了。
因此,
then
回调函数将 Deferred 对象的结果作为参数。 有趣且有帮助。 谢谢你的提示!我搜索了很长时间,但没有成功。 所有解释都太难了。 谢谢!
不错的文章! 我真的很喜欢使用 promises 来避免回调地狱。
但是为什么使用“globalstore”对象?
您可以这样做
示例:http://jsfiddle.net/C4vym/
这对许多程序员来说可能已经司空见惯了,但我找到了这篇关于异步方法与多线程方法区别的文章。 汉堡店中店员为顾客提供汉堡的类比很巧妙,其中烤架和烤箱代表资源。 我想知道它是否真的准确?
http://alookonthecode.blogspot.com/2012/10/simply-explained-multithreaded-vs-async.html
感谢本教程,它确实很有用。 我最近一直在做一个项目,该项目完全是关于加载模板和包含数据的 JSON 以填充它们。 因为我不知道这种技术,所以一切都像这样:加载模板 - 等待 - 加载 JSON - 等待 - 使用数据 - 显示它们。 这确实很有帮助。
在我的项目中,我不知道我尝试使用 Ajax 获取的源是否存在,我不能依赖“then”回调。 如果无法下载源,它将失败。 如果您想知道如何处理这种情况,这就是正确的方法
您可以直接使用 .then() 来实现
https://api.jqueryjs.cn/deferred.then/
如果您喜欢这种类型的东西(我非常喜欢),那么这会让您大吃一惊
例如,您还可以通过 Promises 来排队加载**多个图像**
超出了许多人的需求,但这确实非常棒。
昨天才搜索了 jQuery 的“when”函数,现在我看到了一个关于它的真实教程。 这是一个很棒的功能! 我之前使用过请求嵌套请求的技术,并且对速度感到非常糟糕。 如果我们开发一个在客户端和服务器之间进行通信的应用程序,这将非常有用。
感谢分享。
这也能行吗? 所有 AJAX 回调函数都调用同一个函数,但该函数只有在所有三个回调函数都返回后才会运行。 这意味着 AJAX 调用不需要位于代码中的同一位置,并且可以单独重用。
这并不错误,但对我来说感觉有点奇怪。 就像故意避免结构一样。 但它仍然比嵌套回调快。
David,没错! 您的代码段以相同的方式工作。 请查看上面nekman 的评论。 我认为,promises 更简洁,并且具有更多扩展能力。
David,您的代码有效,但如果您考虑可重用性,则没有必要。 如果您将来要重用这些 AJAX 请求,请将它们放在一个函数中,并为每个 AJAX 调用返回 promise 对象。 您将获得 promises 的所有好处,并且可以在需要时单独调用 AJAX 调用。
感谢大家的反馈。 是的,Benjamin,我喜欢你的方法,比我的方法不那么笨拙。
这大致是 jQuery 在内部执行的操作。 每当一个 promise 准备好时,它就会调用一个函数,直到所有 promises 都完成。
您的代码既不奇怪也不糟糕。 它是原生 JavaScript 中的等效代码,实际上可以避免很多不必要的 jQuery 调用,因此请使用您喜欢的任何一种。
最近我创建了“移动优先”方法演示。 我的示例展示了如何绑定 matchMedia 并根据需要加载资源。 内部逻辑非常简单:使用
<link>
和<script>
标签为移动设备加载资源;绑定到特定的媒体查询,以便在发生指定事件时加载其他资源。 因此,对我来说,它运行得非常完美。我正在使用众所周知的 yepnope.js 来加载 CSS 和 JS。 它提供了良好的抽象层,并避免了回调地狱。
非常好的信息。 它可以减少加载时间并提供良好的用户体验。
我过去常常使用 jQuery 中的回调函数来执行所有 DOM 操作。 但随着项目的越来越大,一切变得如此复杂,以至于它会让你抓狂。 现在我使用 Reactjs 来渲染页面的不同部分,从设计师的角度来看,Reactjs 非常酷,不像 Angularjs 或 Emberjs 等其他复杂解决方案。
虽然这部分很有趣,但在我看来相当棘手
我不同意,更像是
发送请求 #1,发送请求 #2,发送请求 #3。
处理结果 #1,处理结果 #2,处理结果 #3。
由于这些函数调用是异步的,因此最初编写的代码中的请求已经异步发送了。
所以最后我认为它们非常相似。
没关系,我误解了 David Gilbertson 的代码 (https://css-tricks.org.cn/multiple-simultaneous-ajax-requests-one-callback-jquery/#comment-1155012)
感谢这个技巧.. 下次我会尝试。
你的初始代码块有一个多余的
statusCode
层。正确的版本是我在这里使用了几乎相同的技术:http://horiajurcut.com/the-perfect-request/
这对于有条件地快速加载额外内容来说是一个非常有用的技巧。谢谢。 :)
非常有用……谢谢! :)
不错的代码片段。谢谢。
查看评论,谁能详细解释一下 promises 吗?
没什么新东西,但解释非常直接,而且很容易理解。Deferreds 的最佳入门介绍。
你也可以为 jQuery html() 做一个回调函数
它一次可以执行的最大 ajax 调用次数是多少?
服务器负载和客户端对服务器的请求数量应该相同。我还没有测试过这个技巧,但最初我认为这种方法和普通方法之间没有区别。
很好的解释和一些非常有用的提示。谢谢。
这是我倾向于使用的模式
我有一些关于如何将 $.when 与 backbone 结合使用的文章
等待多个集合加载 &
应用程序引导
取消。我的意思是清理我从上面复制的前几行代码
在使用 promises 时,我会看看 Q 和 BlueBird,它们速度最快,对我来说也最有意义。jQuery 的 deferred 方法并没有完全满足 promises 规范,并且是除了原生 JavaScript 实现之外最慢的之一。 http://jsperf.com/promise-comparisons/63
这段文字“在我们的简单用例中,我们可以使用 jQuery 的 $.when() 方法,它接受这些“Deferred”对象的列表(所有 jQuery Ajax 方法都返回 Deferred 对象),然后提供一个回调函数。”后面的代码片段存在问题。你使用了 $.when( … }.then
谢谢!已修复。隐藏,因为与主题无关。
matchMedia。感谢你在那里加入它。看起来爱尔兰人和朋友们也为它提供了一个 polyfill。
最节省时间且清晰的解释和示例。非常感谢!
非常感谢您对使用 jQuery.when() 的如此精彩的解释。
Tusko Trush 提出了这个想法