对异步回调的理解

前几年主要在做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作参数无关。回调只是提供通信的机制。不过在实践中,利用回调函数实现异步调用,是一种非常常见的做法

对异步回调的理解

上一篇:Python求解凸优化问题之CVXPY


下一篇:HDU2196 树状DP