回调函数包裹或者说封装了程序的延续
顺序的大脑
大脑对于事情的计划方式是线性的、阻塞的、单线程的语义,但是回调表达异步流程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度太大。
我们需要一种更同步、更顺序、更阻塞的方式来表达异步,想我们的大脑一样。
信任问题
回调会受到控制反转的影响,因为回调把控制权交给了第三方来调用你代码中的continuation。这会导致一系列麻烦,比如:
- 调用回调过早(在追踪之前)
- 调用回调过晚(或没有调用)
- 调用回调的次数过多或太少
- 没有把所需环境/参数成功传给你的回调函数
- 吞掉可能出现的储物或异常
我们可以用一些方法(可能并不是很好的方法)来解决这些问题。
为了更优雅的处理错误,有些api设计提供了分离回调
function success(data) {
console.log(data)
}
function failure(err) {
console.log(err)
}
ajax('url',success,failure)
另外一种经常处理错误的方式被称做“error-first 风格”
function response(err,data) {
if(err) {
console.err(err)
}
else {
console.log(data)
}
}
ajax('url',response)
如果产生了错误,第一个参数就会被置真。
这种做法并没有真正的解决信任问题。这并为涉及阻止或过滤不想要的重复调用回调函数的问题。而现在的情况是,你可能同时得到成功或者失败的的结果,或者也可能两种都得不到。
完全不调用的信任问题
如果有完全不调用的情况,我们应该设着一个超时来取消事件。
function timeoutify() {
var intv = setTimeout(function(){
intv = null;
fn(new Error("Timeout"))
},delay)
return function() {
if(intv) {
clearTimeout(intv);
fn.apply(this.argument)
}
}
}
function foo(err,data){
if(err) {
console.log(err)
} else {
console.log(data)
}
}
ajax(url,timeoutify(foo,500))
还有一个是信任过早的问题
看这样一段代码
var a = 0;
function result(data) {
console.log(a)
}
ajax(url,result)
a++
打印出来的a是什么呢?我们知道,可能是0也可能是1。
如果是打印出来0,那很有可能是信任过早的问题。因为ajax的请求需要一点时间。如果是0则说明函数没有异步执行。
我们可以用这样的办法来保证函数的异步执行。
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)
}
}
}
ajax(url,asyncify(result))
这样做之后,a的输出只有1。
虽然这些解决问题的办法都没有很完美,但是解决问题的思路是值得借鉴的。理解这些方法,对今后的工作会很有用。
网友评论