如何更好的控制异步请求?相信大家一定首选Promise对象。确实,使用Promise控制异步请求确实非常方便,直接使用then()方法就可以实现当一个异步请求完成后再处理另一个请求或操作。同时,这样的代码也避免了使用大量的回调函数造成的“丑陋的代码”。
不过,在实际的工作中却总是不能尽人意。虽然es6越来越普及,但偶尔也会遇到一些维护老项目的情况,况且Promise的兼容性也是个问题......
恰好我今天也遇到了这个问题,不过情况更加复杂一些。简单描述一下就是——老项目有一个需求就是给一个游戏新增一个小结页,把原来老的小结页(写在iframe里)去掉。直接结果就是页面的按钮减少了,但事件数量不变,且都绑在了一个按钮上。这个按钮点击后意味着做完一道题,就要做一次异步请求来保存答案,然后判断是最后一题的话点击后会触发iframe的postMessage且切换页面,之后还有一次异步请求。由于全部是异步请求,这也直接导致了如果请求速度比较慢的话,页面先postMessage切换页面后,请求直接会shutdown掉!
所以要改的目的也很明确,就是当做到最后一题的时候,先执行完两个异步请求,最后再postMessage切换页面。老司机肯定都知道如果不能用Promise的话直接用回调函数就好了。不过这两个异步请求没有绑定在一起,所以想直接用回调函数也不是那么容易。我想了很久,实在搞不定,后来找leader,leader用一个巧妙的方法帮我搞定了。(我遇到的情况可能比较特殊,不过leader的这个思路确实非常棒)下面直接show给大家看!
$("#next").click(function(){ //每做完一题点击按钮保存
saveQuestion(title); //执行保存游戏的异步操作 if(....){
//执行一些操作
}; if(....){
//执行一些操作
}; if(game.over == true){ //当做到最后一题时会触发
postMessage(title); //iframe postMessage出去并切换页面,此时saveQuestion和saveTask没有完成请求的话会被shut down; saveTask(title); //执行第二个异步请求
} })
如果按照上述代码做的话请求会被关闭掉,原因我也说了,那么此时我们该如何做才能保证两个请求执行完再执行postMessage的操作呢?
$("#next").click(function(){
var flags = {
done:false
};
savaQuestiontitle, flags); if..... if..... if(game.over == true){
var showPostMessage = postMessage.bind(undefined,title);
var callback = function(flags,func){
while(flags.done == false){};
func();
}
callback = callback.bind(undefined,flags,postMessage);
saveTask(title,callback);
}
})
只要把代码写成这样就可以成功运行了!有的朋友可能看不懂,别急,我一句一句来分析!
首先先写一个flags对象,将flags作为参数传入异步请求的函数中,这个对象的作用主要用来控制异步请求的完成。只有当saveQuestion内的请求success或error后,将flags的done属性变为true;之后便是一系列if的判断,我们暂且省略;直到游戏结束时,我们声明一个回调函数callback ,这个回调函数传入两个参数,一个是flags对象,另一个是函数(即posMessaget);callback内部先是使用一个while循环,在flags.done为false时不停的循环,什么也不做。根据前面我们知道只有在saveQusetion异步请求结束后flags.done才会为true,所以说这个回调函数是为了控制saveQuestion的;
最后,我们再将回调函数放入saveTask中作为第二个参数,只有当saveTask请求结束后才调用callback!这样一来我们就可以保证整个程序先执行saveQuestion,虽然不知道saveQuestion什么时候请求结束,但可以通过flags.done来判断;之后执行saveTask,只有执行完saveTask后才会执行回调函数;如果此时saveQuestion请求还没结束,callback会在内部使用while等待请求,只有成功后才会执行最后的postMessage!