作者在之前的一篇博客中简单讨论了Python和Javascript的异同点。事实上,作为一种编程语言,Javascript异步编程是一个值得讨论的有趣话题。JavaScript异步编程简介回调函数与异步执行所谓异步就是函数调用不直接返回执行结果,往往是通过回调函数异步执行。我们先看看回调函数是什么:varfn=function(callback){//dosomethinghere...callback.apply(this,para);};varmycallback=function(parameter){//dosometingincustomercallback};//callthefnwithcallbackasparameterfn(mycallback);回调函数其实就是调用用户提供的函数,往往以参数的形式提供。回调函数不必异步执行。比如上面的例子,回调函数是同步执行的。大多数语言都支持回调。C++可以使用函数指针或回调对象,而Java一般使用回调对象。在Javascript中有许多通过回调函数执行的异步调用,例如setTimeout()或setInterval()。setTimeout(function(){console.log("thiswillbeexecutedafter1second!");},1000);上面的例子中,setTimeout直接返回,匿名函数会在1000毫秒后(不一定保证是1000毫秒)异步触发并执行,完成打印控制台的操作。也就是说,在异步操作的上下文中,函数直接返回,控制权交给回调函数,回调函数会被调度在未来某个时间片执行。那么为什么需要异步呢?为什么不能直接在当前函数中进行操作呢?这需要了解Javascript的线程模型。Javascript线程模型和事件驱动Javascript最初是为了协助在浏览器中提供HTML交互功能而设计的。所有的浏览器都包含一个Javascript引擎,Javascript程序运行在这个引擎中,只有一个线程。单线程可以带来很多好处,程序员可以很开心不用去想多线程阻塞编程需要面对的烦人问题,比如资源同步和死锁。但是很多人会问,Javascript既然是单线程的,那怎么异步执行呢?这就需要了解Javascript在浏览器中的事件驱动(eventdriven)机制。事件驱动一般通过事件循环和事件队列来实现。假设在浏览器中有一个专门用于事件派发的实例(这个实例可以是一个线程,我们可以称之为事件派发线程),这个实例的工作就是一个死循环,取出Event,它处理所有的事件——相关的回调函数(事件处理程序)。注意回调函数是在Javascript的主线程中运行的,而不是在事件分发线程中运行的,这样才能保证事件处理不会被阻塞。事件循环代码:while(true){varevent=eventQueue.pop();if(event&&event.handler){event.handler.execute();//executethecallbackinJavascriptthread}else{sleep();//sleepsometimetoreleasetheCPUdootherstuff}}由事件机制驱动,我们可以想象Javascript的编程模型是响应一系列事件并执行相应的回调函数。许多UI框架都采用这种模型(例如JavaSwing)。那为什么要异步,同步不是很好吗?异步的主要目的是处理非阻塞。在与HTML交互的过程中,会需要进行一些IO操作(典型的有Ajax请求,脚本文件加载)。如果这些操作是同步的,就会阻塞其他操作,提高用户体验。该页面没有响应。综上所述,Javascript通过事件驱动机制在单线程模型下以异步回调函数的形式实现了非阻塞IO操作。Javascript异步编程挑战Javascript的单线程模型有很多好处,但也带来了很多挑战。代码可读性想象一下,如果一个操作需要多个非阻塞IO操作,并且每个结果都通过回调传递,那么程序可能是这样的。operation1(function(err,result){operation2(function(err,result){operation3(function(err,result){operation4(function(err,result){operation5(function(err,result){//dosomethinguseful})})})})})我们称之为意大利面条代码。这样的代码很难维护。这种情况在服务器端的情况下会比较多。异步进程控制带来的另一个问题是进程控制。比如我要访问三个网站的内容。当获取到三个网站的所有内容后,会进行合并处理,然后发送到后台。代码可以这样写:varurls=['url1','url2','url3'];变量结果=[];for(vari=0,len=urls.length();i
