其实工作中一直都有异步流程控制的需求,主要是各种需要顺序执行的AJAX。
之前请求不多的时候,我就直接简单粗暴地在回调里调用下一个请求函数,随着项目越来越大,越来越复杂,这种方式难以维护的缺点就越发的明显。大热的ES6中的promise方法的确很强,然而,需要兼容IE7(现在是IE10了)的我流下了两行清泪....而且我们项目还未引入打包和配置工具(没错就是这么落后),所以没有办法愉快地使用Promise等ES6语法和对象。
这个问题一直非常困扰我,想了很久都没有比较好的解决办法,直到有一天,看到了一个CSS3动画的制作,依赖JQ,控制异步动画的时候完全使用了deferred对象,仿佛打开了异步流程新世界的大门
deferred对象的使用方法
- 创建Deferred对象
- 在异步流程结束后改变deferred对象状态
- 返回deferred对象
function F(){
var def = $.Deferred(); //创建deferred对象
setTimeout(function(){
console.log("hi~");
def.resolve(); //改变deferred对象状态为“已完成”
},50);
return def; //返回deferred对象
}
然后在需要的地方调用这个异步函数,使用then
表示该方法执行完后再执行下一步
F().then(function(){
//do something you want
});
恩,妈妈再也不用担心我的异步了,即便是有多个异步回调需要控制,也能使用连续的then
写出漂亮的顺序流
F1().then(function(){
F2();
}).then(function(){
F3();
});
似乎回调依赖的问题已经解决了,直到有一天,我遇到了一个循环异步回调
一个流程依赖多个异步回调
大致需求是,有一个循环体,里面的循环内容是异步流程,大概可以写成这样
for(var i = 0 ;i < 10 ; i++){
setTimeout(function(){
console.log("我要吃饭啦~"+i);
});
}
console.log("我吃饱啦~")
promise
对象里有个all
,可以用放入数组的形式控制多个异步依赖,没有编译工具又要保证兼容性的我再次留下两行清泪。。研究了半天,好像没有在Deferred对象中发现有类似的方法。
于是先是尝试着,还是使用单个流程控制的方法去写
function wait(){
var deferred = $.Deferred();
for(var i = 0 ;i < 10 ; i++){
setTimeout(function(){
console.log("我要吃饭啦~"+i);
deferred.resolve();
});
}
return deferred;
}
wait().then(function(){
console.log("我吃饱啦~")
})
emmmmm当然这是不对的了,循环第一次的时候,deferred状态就已经更改为完成,所以就会执行“我吃饱啦”,然后再继续执行剩余的9次。
于是,我做了如下改写,使用变量count作为计数标识,当计数循环完成之后,再改变deferred对象的状态。
function wait(){
var dtd = $.Deferred()
var count = 0;
for(var i = 0 ;i < 10 ; i++){
setTimeout(function(){
console.log("我要吃饭啦~"+i);
count++;
if( count == 10 ){
dtd.resolve();
}
},500);
}
return dtd;
}
wait().done(function(){console.log("我吃饱啦")})
运行10次“我要吃饭啦”,随后运行“我吃饱啦”
成功啦~给自己一朵小红花!
兼容性测试
最后的最后,在IE的各个环境下进行测试,居然在IE5下也能跑。所以只要是能支持JQ的Deferred对象,一般情况下这种异步流程操作方式都是可行的。
ps
:感觉自己是走了歪门邪道,不知道deferred
对象中是不是有类似于promise
对象中的all
可以直接控制多个异步,如果有的话,还希望各位大大能指点迷津
pss
:关于公司还是使用非常传统开发方式的问题,只能说上有政策的阻碍,下有业务的局限。原因很复杂,一时半会儿也说不清,所以只能找机会跑路啦哈哈哈哈
学习资料:
jQuery的deferred对象详解——阮一峰