JavaScript异步编程通常的方法是指定一个回调函数,当操作结束之后,自动执行回调函数。但是这样有个不好的一点,也就是容易造成回调函数嵌套,比如下面一个搞笑的情况:

              ......
			   ......
                ......
                           }
                         }
                       }
                     }
                   }
                 }
               }
......
......

为了避免这种情况的出现,业界现在有几种编程范式。如CommonJS提出的Promise模式,就是个不错的解决方案。jQuery的deferred对象就是jQuery的Promise范式回调函数解决方案。

deferred对象与传统回调函数的对比

这里用AJAX请求做示列,传统写法:

$.ajax({
    url: "test.html",
    success: function(){
      alert("哈哈,成功了!");
    },
    error:function(){
      alert("出错啦!");
    }
  });

deferred对象写法:

$.ajax("test.html")
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });

这里,实际上.done方法相当与success方法,.fail方法相当于error方法。从这里可以看出,deferred对象对于整个代码结构来说会更清晰明了,同时更可以采用链式写法,$.ajax('test.html').done(...).fail(...).done(...),操作起来是非常方便的。

同时为多个异步操作指定回调函数

jQuery提供$.when方法来为多个事件指定一个回调函数,写法如下:

$.when($.ajax("test1.html"), $.ajax("test2.html"))
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });

当两个请求都成功后会执行.done方法,失败则会执行.fail方法,实际上,$.when返回的就是一个deferred对象。

Deferred对象方法

初步了解deferred对象的操作后,我们可以看下deferred包含的一些方法:

  1. $.Deferred生成一个deferred对象。
  2. deferred.done指定操作成功时的回调函数
  3. deferred.fail指定操作失败时的回调函数
  4. deferred.promise没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。
  5. deferred.resolve手动改变deferred对象的运行状态为”已完成”,从而立即触发.done方法。
  6. deferred.reject这个方法与deferred.resolve正好相反,调用后将deferred对象的运行状态变为”已失败”,从而立即触发.fail方法。
  7. $.when为多个操作指定回调函数。
  8. deferred.then有时为了省事,可以把.done.fail合在一起写,这就是.then方法。
  9. deferred.always这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve还是deferred.reject,最后总是执行。

自定义的回调函数

有了上面的deferred方法后,我们可以这样定义自己的异步操作:

var wait = function(dtd){
    var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
    var tasks = function(){
      alert("执行完毕!");
      dtd.resolve(); // 改变Deferred对象的执行状态
    };

    setTimeout(tasks,5000);
    return dtd.promise(); // 返回promise对象
  };
  $.when(wait())
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });