什么是 deferred 对象
deferred 对象是 jQuery 为操作的回调函数提供的解决方案。deferred 对象提供了链式操作来对操作进行回调处理。
deferred 对象引入了“执行状态”这个概念,一个 deferred 对象的执行状态分为三种
- 未完成(unresolve)
- 已完成(resolved)
- 已失败(rejected)
通过 deferred 对象的 resolve,reject 方法来改变执行状态从而调用与该状态绑定的回调函数(们)。
注意:deferred 对象状态一旦被 resolve 或 reject 便不再次修改。也就是说,resolve 或者 reject 方法只能调用一次。多次调用只执行一次(jQuery 的实现是这样的,不代表所有的 deferred 实现都是如此,此行为不可预测,可能导致崩溃)
deferred 有什么好处
- 避免了层层嵌套的“回调地狱”
- 提供了可以安排顺序的回调处理方式
- 链式操作可以让回调处理更加的自由,可添加任意个数的回调函数
deferred 用法
定义一个 deferred 对象
1 2 3 4 5 6 7 8 9 10 11 12
| var wait = function(dtd) { var dtd = $.Deferred(); setTimeout(function() { this.time = 1; dtd.resolve(); }, 1000); dtd.progress(processing); for (var i = 0; i < 10; i++) { dtd.notify() } return dtd.promise(); };
|
提供 proimise()
promise() 方法实际上是在原来的 deferred 基础上返回一个只开放不能改变执行状态的方法(如 done() 和 fail() )的 deferred 对象,使得执行状态无法被非执行者改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var wait = function(dtd) { var dtd = $.Deferred(); setTimeout(function() { dtd.resolve(); }, 1000); return dtd.promise(); }; $.when(wait()) .done(function() { console.log("哈哈,成功了!"); }) .fail(function() { console.log("出错啦!"); })
|
为同一操作提供多种状态的回调处理
done 处理 resolved 状态
fail 处理 rejected 状态
他们都可以接受参数,通过 resolve() 或者 reject() 方法将数据传递给回调方法。
1 2 3
| $.when(wait()) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
|
控制回调处理的先后顺序
这里分为两种情况:
- 回调处理是同步的
直接使用多个 then 或者 done 方法的链接就可以实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| $.when(wait()) .done(function() { console.log("1 return!"); }) .done(function(time) { console.log("2 return!"); }) .done(function(time) { for (var i = 0; i < 1000000000; i++) { } console.log("3 return!"); })
|
或者
1 2 3 4 5 6 7 8 9 10 11 12 13
| $.when(wait()) .then(function() { console.log("1 return!"); }) .then(function(time) { console.log("2 return!"); }) .then(function(time) { for (var i = 0; i < 1000000000; i++) { } console.log("3 return!"); })
|
需要注意的是:使用 done 方法的场景是几种回调的处理是在同一层次上的,done 方法不能通过 return 来将值传递给后续的 done 方法。
then 方法的使用场景是几个回调在不同层次上处理返回的数据,then 方法中的参数可以通过 return 语句将处理过的数据传递到下一级 then() 中的回调函数中。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $.when(wait()) .then(function() { console.log("1 return!"); return 1; }) .then(function(time) { console.log("2 return!"+time); return 2; }) .then(function(time) { console.log("3 return!"+time); }) /** * 输出: * 1 return! * 2 return1! * 3 return2! */
|
- 回调处理是异步的
这时需要使用 then 方法,并在每个 then 方法中传入 deferred 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| $.when(wait()) .then(function() { var dtd = $.Deferred(); setTimeout(function() { console.log("1 return!"); dtd.resolve(); }, 1000); return dtd.promise() }) .then(function(time) { var dtd = $.Deferred(); setTimeout(function() { console.log("2 return!"); dtd.resolve(); }, 1000); return dtd.promise() }) .then(function(time) { var dtd = $.Deferred(); setTimeout(function() { console.log("3 return!"); dtd.resolve(); }, 1000); return dtd.promise() })
|
为多个操作提供统一的回调处理
将多个操作合并返回,只有当全部操作都 resolved 之后才会执行 done 函数。
1 2 3
| $.when(wait1(), wait2() .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
|
为普通操作提供回调函数
使用 deferred 对象不仅可以为异步过程提供回调,也可以为同步过程提供回调。
1 2 3 4 5 6
| var wait2 = function(dtd) { var dtd = $.Deferred(); syncOp() dtd.progress(processing); return dtd.promise(); };
|
在处理过程中返回信息
使用 deferred 对象的 progress() 方法指定回调函数。在过程执行中调用 deferred 对象的 notify() 方法可以调用这个回调函数。
使用场景如:网络请求数据之前先取缓存数据,取到数据后通知控制器更新视图。然后发起网络请求,请求返回后 resolve() 将数据传递给控制器再次更新视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var wait = function(dtd) { var dtd = $.Deferred(); //TODO: 取网络数据 dtd.progress(processing); dtd.notify()//通知控制器更新视图 //模拟发起网络请求 setTimeout(function() { this.time = 1; dtd.resolve();//返回数据后 resolve() }, 1000); return dtd.promise(); };
$.when(wait()) .done(function(){ alert("哈哈,成功了!,用网络数据再次更新视图"); }) .fail(function(){ alert("出错啦!"); }); function processing() { console.log('取缓存成功,更新视图'); }
|