jQuery学习笔记

泛化回调

前面已经介绍过的 AJAX 是一种典型的异步回调的过程,我们发出请求,然后定义了当请求返回时要做什么事。显然,同样的机制可以应用于任何的地方,特别是在于 javascript 这种本来大部分时候都处于异步的运行环境中时,回调的方式更是大有用武之地。

好了,其实我要说的就是在除 AJAX 之外的地方,也应该有机制来支持我们使用异步回调。

Deferred

Deferred 对象是在 jQuery1.5 中引入的回调管理对象。其作用,大概就是把一堆函数按顺序放入一个调用链,然后根据状态,来依次调用这些函数。 AJAX 的所有操作都是使用它来进行封装的。比如我们定义的,当请求正常返回时,会调用 success 定义的函数,失败时,会调用 error 定义的函数。这里的“失败”,“正常”就是状态,而对应的函数,只是调用链中的一个而且。

先来看一个直观的例子:

var obj = $.Deferred(function(a){});
obj.done(function(){console.log('1')});
obj.done(function(){console.log('2')});
obj.resolve();

这样,我们就可以按顺序看到 1 , 2 这两个输出了。

总的来说, jQuery 的 Deferred 对象有三个状态: done , fail , process 。

  • process 只能先于其它两个状态先被激发。
  • done 和 fail 互斥,只能激发一个。
  • process 可以被重复激发,而 done 和 fail 只能激发一次。

然后, jQuery 提供了一些函数用于添加回调,激发状态等:

deferred.done()
添加一个或多个成功回调。
deferred.fail()
添加一个或多个失败回调。
deferred.always()
添加一个函数,同时应用于成功和失败。
deferred.progress()
添加一个函数用于准备回调。
deferred.then()
依次接受三个函数,分别用于成功,失败,准备状态。
deferred.reject()
激发失败状态。
deferred.resolve()
激发成功状态。
deferred.notify()
激发准备状态。
如果一个 Deferred 已经被激发,则新添加的对应的函数会被立即执行。

除了上面的这些操作函数之外, jQuery 还提供了一个 jQuery.when() 的回调管理函数,可以用于方便地管理多个事件并发的情况,先看一个 AJAX 的“原始状态”例子:

var defer = $.ajax({
  url: '/json.html',
  dataType: 'json'
});

defer.done(function(data){console.log(data)});

.done() 做的事和使用 success 定义是一样的。

当我们需要完成,像“请求A和请求B都完成时,执行函数”之类的需求时,使用 $.when() 就可以了:

var defer_1 = $.ajax({
  url: '/json.html',
  dataType: 'json'
});

var defer_2 = $.ajax({
  url: '/jsonp.html',
  dataType: 'jsonp'
});

var new_defer = $.when(defer_1, defer_2);
new_defer.done(function(){console.log('haha')});

在 $.when() 中的 Deferred ,只要有一个是 fail ,则整体结果为 fail 。

Deferred 的回调函数的执行顺序与它们的添加顺序一致。

这里特别注意一点,就是 done / fail / always 与 then 的返回值的区别。从功能上看,它们都可以添加回调函数,但是,方法的返回值是不同的。 前组的返回值是原来的那个 defer 对象,而 then 返回的是一个新的 defer 对象。

then 返回新的 defer 这种形式,可以用于方便地实现异步函数的链式调用。

比如对于:

var defer = $.ajax({
    url: '/json',
    dataType: 'json'
});

如果使用 done 方法:

defer.done(function(){
  return $.ajax({
    url: '/json',
    dataType: 'json',
    success: function(){
      console.log('inner')
    }
  });
}).done(function(){
  console.log('here');
});

等同于是调用了两次 defer.done , defer.done ,注册的两次回调函数依次被执行后,我们看到的输出是:

here
inner

这是两次 defer.done 的结果,第一个回调函数返回了一个新的 defer 没任何作用。

如果换成 then 方法的话:

defer.then(function(){
  return $.ajax({
    url: '/json',
    dataType: 'json',
    success: function(){
      console.log('inner')
    }
  });
}).done(function(){
  console.log('here');
});

上面的代码相当于:

var new_defer = defer.then(...);
new_defer.done(...);

它跟两次 defer.done 是不同的。 new_defer 会在 inner 那里的 defer 被触发时再被触发,所以输出结果是:

inner
here

更一般地来说 then 的行为,就是前面的注册函数的返回值,会作为后面注册函数的参数值:

var defer = $.ajax({
  url: '/json',
  dataType: 'json'
});

defer.then(function(res){
  console.log(res);
  return 1;
}).then(function(res){
  console.log(res);
  return 2;
}).then(function(res){
  console.log(res);
});

上面代码的输入结果是:

ajax response
1
2

Callbacks

讲了 Deferred ,现在再来看 Callbacks ,这才是本源。事实上, Deferred 机制,只是在 Callbacks 机制的上层进行了一层简单封装而且。 Callbacks 对象,才是真正的 jQuery 中定义的原始的回调管理机制。

var obj = $.Callbacks();
obj.add(function(){console.log('1')});
obj.add(function(){console.log('2')});
obj.fire();

Callbacks 对象的初始化支持一组控制参数:

$.Callbacks( flags )
初始化一个回调管理对象。 flags 是空格分割的多个字符串,以定义此回调对象的行为:

  • once 回调链只能被激发一次
  • memory 回调链被激发后,新添加的函数被立即执行
  • unique 相同的回调函数只能被添加一次
  • stopOnFalse 当有回调函数返回 false 时终止调用链的执行

Callbacks 的控制方法:

callbacks.add()
添加一个或一串回调函数
callbacks.fire()
激发回调
callbacks.remove()
从调用链中移除指定的函数
callbacks.empty()
清空调用链
callbacks.disable()
关闭调用链的继续执行,新添加的函数也不会被执行
callbacks.lock()
锁定调用链,但是如果打开了 memory 的 flag ,新添加的函数仍然会执行
callbacks.has()
检查一个函数是否处于回调链之中
callbacks.fired()
检查调用链是否已经被激发
callbacks.locked()
检查调用链是否被锁定

上一篇: 最后文章