Javascript和Ruby中的回调

看到一片文章讲的是Javascript的异步回调,然后我想Javascript的callback是不是都是异步的? 找到一篇阮一峰的文章, 里面的一段代码其实有点问题。

在回调函数中的一个例子其实不能反应出回调的异步。

function f1(callback){
    setTimeout(function () {
        // f1的任务代码
        callback();
    }, 1000);
}

function f2(){};

f1(f2);

文章中说,“采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。”, f2作为f1的callback,其实是f2可能阻塞程序运行。我改一下这个例子,可以更清楚地表现异步。

function f2(){
    setTimeout(function(){console.log('f2')}, 1000);
};

function f1(callback){
    console.log('start f1');
    callback();
    console.log('end f1')
};

f1(f2);

执行f1(f2)之后,程序会在打印start f1end f1之后,大概等待一秒,最后打印出f2,所以表明f2是异步执行的,需要花费一秒的f2并没有阻塞f1的执行,相当于把f2放到后台去执行,这个就是异步回调。

经过朋友_kaichen的提醒,上面的例子代码不能说明Javascript的callback是异步的,因为上面的异步操作是函数setTimeout带来的,事实上Javascript的callback默认不是异步的。那就写个例子来说明一下,既然setTimeout函数是异步处理的,那我们就模拟一个同步的sleep函数来看看Javascript的callback到底是同步还是异步的。

function sleep(delay) {
    var start = new Date().getTime();
    while (new Date().getTime() < start + delay);
}

function f2(){
    console.log('f2');
    sleep();
};

function f1(callback){
    console.log('start f1');
    callback();
    console.log('end f1')
};

f1(f2);

运行下这个例子可以看到,打印出start f1f2,之后等了大概一秒,然后打印出end f1f1确实被耗时长的f2阻塞了,这表明f2这个callback是同步执行的。

然后我就想Ruby中有没有像Javascript中一样的回调呢?我在SlideShare上找到一个Slide http://www.slideshare.net/TheNaoX1/java-script-callbacks-in-ruby 这个Slide讲Ruby中的block就相当于Javascript中的回调。写个简单的例子试一下就知道了。

def callback
  sleep(3)
  puts "this is callback"
end

def do_something
  puts "start do_something"
  yield
  puts "end do_something"
end

do_something{callback()}

如果这个yield是异步执行的话,那打印start do_something之后,会立即打印end do_something,然后等待大概3秒左右,会打印this is callback。但运行的事实是打印了start do_something之后,等了大概三秒,然后打印this is callback,接着立即打印了end do_something。这样说明callback是和do_something里面其他代码同步执行的,只有当callback执行完成后,后面的代码才能执行,也就是阻塞了程序的运行。