前几年主要在做JAVA开发,对回调接触得不多。最近几个月更多地使用了javascript和objective-c,本文总结一下我的粗浅理解
回调
java并不是不能实现回调,只是比较麻烦,并且也不是惯用法,至少我这5年几乎没有遇到过必须使用的场景。如果一定需要的话,也可以实现:
public void doSomething(Callable callback){ // 做一些逻辑 callback.xxx();// 回调 // 做另外一些逻辑 }
如果引入Thread类,也可以实现异步回调,代码省略。只是,很难模拟真正的闭包,所以意义就小了很多,一般大家都不这么做
而在javascript和objective-c中,特别是前者,回调就很常见了。总结起来,实现回调的主要方法有2种,分别是callback和delegate。js里callback用得比较多,而oc里两者都很常见
callback
函数在js和oc里都是一等公民,所以实现callback很容易。在js里就是一般的function,oc用的是block
-(void) doOperation:(void(^)(FMDatabase*))block; -(void) doOperation:(void(^)(FMDatabase*))block { [db open]; block(db); [db close]; }
上面的doOperation()方法,就接受一个block作为参数,在合适的时候去回调它
delegate
delegate模式在oc里用的很多,包括许多原生的API,也是使用delegate实现回调的,比如UITableViewDelegate等
-(void) doSomething { // do something [delegate someMethod]; // do something }
比较
总的来说,这2种机制的目的,都只是提供两个对象之间通信的方式。A对象在调用B对象上的方法时,传一个回调函数作为参数,或者将自己设置为B的delegate,都使B在需要的时候,可以反过来调用A的方法
delegate的好处是比较灵活,而且由于一个对象可以被组合和继承,还可以同时作为多个对象的delegate,所以复用性也比较好
但是callback最好的一点在于,可以利用闭包的特性,因此传参特别方便,考虑下面的代码:
- (void)search:(CDVInvokedUrlCommand*)command { [printer scanPrinterStart:^(void){ // do something [command xxx]; }]; }
在block内部,仍然可以访问外围的局部变量(参数,临时变量等),实现了一个闭包,不需要额外传参,因此很方便。这种场景下,如果使用delegate,就要想办法把command也传过去,写起来会比较麻烦
2种方式各有优劣,所以IOS很多的原生API,都同时提供了delegate和callback接口,让用户可以根据实际情况选择
异步
以前我总是把异步和回调混在一起,错误地理解为:回调一定是异步的。其实并非如此
前面说了,回调只是2个对象之间通信的方式,不代表一定是同步或者异步的。具体究竟是同步还是异步,要看实现,比如下面2段代码,都采用了回调,但是前者是同步的,后者是异步的
example(function(){ console.log("called"); }); // 后续操作,将被example()调用阻塞 function example(callback){ // 耗时操作 callback(); // 耗时操作 }
-(void) test { [self example:^(void){ NSLog(@"called"); }]; // 下面的操作立刻执行,不会被example()调用阻塞 } -(void) example:(void(^)(void))callback { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ // 耗时操作 callback(); }); return; }
上面2段示例代码,都采用了回调函数的方式通信,但是行为却有很大差别。前者是同步的,因此会阻塞后续的代码;而后者的example函数是立刻返回的,test函数里后续的操作不会被阻塞
所以,是同步还是异步,与回调函数没有必然联系。只是由于在javascript,特别是node.js里,几乎95%的回调函数都是异步的,所以在某种程度上造成了误解
总结
要实现2个对象之间的通信,回调和delegate都是很好的方式,由于回调可以利用闭包的特性,所以我觉得更方便一点
判断一个函数是同步的还是异步的,要看它是立刻返回,还是会长时间阻塞,与它是否接受callback function作参数无关。回调只是提供通信的机制。不过在实践中,利用回调函数实现异步调用,是一种非常常见的做法