目录
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()
检查调用链是否被锁定