5-2 Deferred是什么?
本节编程练习不计算学习进度,请电脑登录imooc.com操作

Deferred是什么?

前端项目的开发,不仅仅涉及到同步的概念,而且还会经常穿插各种异步的处理。一些大的操作,比如远程获取数据,操作一个大数据处理,这时候是不能马上获取到数据的。假设我们发送一个AJAX请求到接受到数据需要10秒钟,那么从发送到接受数据这个时间段中,前端的处理时间其实是空闲,但是对于开发者来说这种时间是不能浪费了,所以我们可以在10秒钟做很多同步的处理,同时等待异步的数据返回。所以我们需要监听这个回调的数据在成功的时候能够获取到,或者设计一个返回后触发处理的机制,当然原生的JavaScript对这个机制几乎是没有的。为了优化这个形成统一的异步处理方案,jQuery就开始设计了一个Deferred异步模型。

    Deferred 提供了一个抽象的非阻塞的解决方案(如异步请求的响应),它创建一个promise对象,其目的是在未来某个时间点返回一个响应。简单来说就是一个异步/同步回调函数的处理方案。

$.Deferred在jQuery代码内部有四个模块被使用,分别是“promise方法”、“DOM ready”、“Ajax模块”及“动画模块”。

看看jQuery中的最常用的AJAX处理:

一:Ajax的改造

传统的jQuery的AJAX操作的传统写法(1.5版之前):

$.ajax({
  url: "aaron.html",
  success: function(){
     alert("成功!");
  },
  error:function(){
    alert("失败!");
  }
})

$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数。

在1.5版本后通过新的Deferred引入就改成了:

$.ajax("aaron.html")
.done(function(){ alert("成功"); })
.fail(function(){ alert("出错"); });

把传参的回调,换成了链式的写法,这样可读性更高了。在jquery 1.5版后,通过$.ajax返回的不是XHR对象了,而是经过包装的Deferred对象,所以就具有promise的一些规范。当然这种写法到底是怎么做的,我们在后续的教程中会详细的讲解到。

二:提供一种方法来执行一个或多个对象的回调函数

在实际开发中,我们可能要发送多个异步的请求操作,我们需要等所有的异步都处理完毕后,才能继续下一个动作。如右边代码所示。

所以我们这里要涉及一个等待的处理。我们自己要做一个计时器,每一个任务执行完毕后,都要触发一次任务的检测。当最后一个调用完毕了,我们就可以执行后面的动作,当前这里的写法也会有些问题,比如错误的时候没有处理。同样的功能,我们换成Deferred就会很简单了。

$.when($.ajax("a1.html"), $.ajax("a2.html"))
  .done(function(){ alert('2次回调都正确返回了') })
  .fail(function(){ alert('出错了'); });

这段代码的意思是:先执行两个操作$.ajax("a1.html")和$.ajax("a2.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

三:可以混入任意的对象接口中

jQuery的Deferred最好用的地方,就是模块化程度非常高,可以任意配合使用。

function task(name) {
  var dtd = $.Deferred();
  setTimeout(function() {
    dtd.resolve(name)
  }, 1000)
  return dtd;
}
$.when(task('任务一'), task('任务二')).done(function() {
  alert('成功')
})

把需要处理的异步操作,用Deferred对象给包装一下,然后通过when方法收集异步的操作,最后再返回出done的成功,这样的处理太赞了!

所以说,Deferred的引入,为处理事件回调提供了更加强大并且更灵活的编程模型。

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <script src="http://code.jquery.com/jquery-latest.js"></script>
  6. <title></title>
  7. </head>
  8. <body>
  9.  
  10. <button id="aaron1">运行案例一</button>
  11.  
  12. <button id="aaron2">运行案例二</button>
  13.  
  14. <script type="text/javascript">
  15.  
  16.  
  17. //提供一种方法来执行一个或多个对象的回调函数
  18. //在实际开发中,我们可能要发送多个异步的请求操作,我们需要等所有的异步都处理完毕后,才能继续下一个动作
  19.  
  20. //案例一
  21. function task1(name, fn) {
  22. setTimeout(function() {
  23. fn(name)
  24. }, 500)
  25. }
  26.  
  27. function task2(name, fn) {
  28. setTimeout(function() {
  29. fn(name)
  30. }, 1000)
  31. }
  32.  
  33. //任务数
  34. var taskNuns = function() {
  35. var num = 2; //2个任务
  36. return function() {
  37. if (num === 1) {
  38. show('任务都完成了',$("#aaron1"))
  39. }
  40. num--;
  41. }
  42. }()
  43.  
  44.  
  45. $("#aaron1").click(function() {
  46. //常规处理
  47. task1('任务一', function() {
  48. show('task1', $("#aaron1"))
  49. taskNuns()
  50. })
  51.  
  52. task2('任务二', function() {
  53. show('task2', $("#aaron1"))
  54. taskNuns();
  55. })
  56. })
  57.  
  58.  
  59.  
  60.  
  61. //========================分割线============================================
  62.  
  63. //案例二
  64. //通过Deferred改进
  65. function task3(name) {
  66. var dtd = $.Deferred();
  67. setTimeout(function() {
  68. show('task3执行完毕',$("#aaron2"))
  69. dtd.resolve(name)
  70. }, 500)
  71. return dtd;
  72. }
  73.  
  74. function task4(name) {
  75. var dtd = $.Deferred();
  76. setTimeout(function() {
  77. show('task4执行完毕',$("#aaron2"))
  78. dtd.resolve(name)
  79. }, 1000)
  80. return dtd;
  81. }
  82.  
  83. $("#aaron2").click(function() {
  84. $.when(task3('task1'), task4('task2')).done(function() {
  85. show('when处理成功', $("#aaron2"))
  86. })
  87. })
  88.  
  89.  
  90. function show(data, ele) {
  91. (ele || $("body")).append('<li>' + data + '</li>')
  92. }
  93.  
  94. </script>
  95.  
  96. </body>
  97. </html>
下一节