嵌套回调与链式回调
嵌套回调
listen('click',function hadler(evt){
setTimeout(function request(){
ajax('http://some.url.1',function response(text){
if(text == 'hello'){
handler();
}else if(text == 'world'){
request();
}
})
},500);
})
这种代码常常被称为回调地狱,但并不是嵌套导致跟踪异步如此之难。
链式回调
listen('click',handler);
function handler(){
setTimeout(request,500)
}
function request(){
ajax('http://some.url.1',response);
}
function response(text){
if(text == 'hello'){
handler();
}else if(text == 'world'){
request();
}
}
尽管把代码编码成线性的,但在整个代码跳来跳去来“查看”流程,使追踪的难度增高。
信任问题
控制反转
有时候如ajax(...)这样的第三方提供的代码不是你编写的代码,也不在你的直接控制之下,也就是把自己程序一部分的执行控制交给某个第三方,这种情况称为控制反转。
控制反转的问题
- 调用回调过早(在追踪之前)
- 调用回调过晚(或没有调用)
- 调用回调的次数太少或太多
- 没有吧所需的环境/参数成功传给你的回调函数
- 吞掉可能呈现的错误或异常
鉴于上面出现的这些问题,我们不得已构建全部的安全机制,进行相关值的检测,而且通常为每个异步回调重复这样的工作最后成为了负担
回调设计的变体
分离回调
即一个用于成功通知,一个用于出错通知
function succuess(data){
console.log(data)
}
function failure(err){
console.error(err);
}
ajax('http://some.url.1',success,failure)
ES6 Promise API使用的就是这种分离回调
error-first风格(Node风格)
回调的第一个参数保留用作错误对象(如果有的话),成功的话,这个参数就会被清空/置假(后续的参数就是成功的数据),如果产生了错误结果,那么第一个参数就会被置起/置真
fucntion response(err,data){
if(err){
console.error(err);
}else{
console.log(data)
}
}
ajax('http://some.url.1',response)
解决完全不调用问题
function timeoutify(fn,delay){
var intv = setTimeout(function(){
intv = null;
fn(new Error("Timeout!"));
},delay);
return function(){
if(intv){
clearTimeout(intv);
fn.apply(this,arguments);
}
}
}
function foo(err,data){
if(err){
console.error(err);
}else{
console.log(data)
}
}
ajax('http://some.url.1',timeoutify(foo,500));
解决调用过早(缓存)
function asyncify(fn){
var orig_fn = fn,
intv = setTimeout(function(){
intv = null;
if(fn) fn();
},0);
fn = null;
return function(){
if(intv){
fn = orig_fn.bind.apply(orig_fn,[this].concat([].slice.call(arguments)));
}
}
else{
orig_fn.apply(this,arguments);
}
}
function result(data){
console.log(a);
}
var a = 0;
ajax('..pre-cached-url..',asyncify(result));
a++;
不管这个Ajax请求已经在缓存中并试图对回调立即调用,还是要从网络上取得,进而在将来异步完成,这段代码总是会输出1.
上文为学习《你不知道的JS中卷》记录的笔记
网友评论