Javascript知识整理——回调

嵌套回调与链式回调

嵌套回调
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中卷》记录的笔记

你可能感兴趣的:(Javascript知识整理——回调)