JS为什么适合解决阻塞问题
先看个例子(JS代码):
//根据name,在数据库中PERSON表中查出phoneNum var phoneNum = db.query("select num from person where name='aldrich'"); //进程等待数据查询完毕,然后使用查询结果 output("phoneNum ");
在上面代码的两个语句之间,整个数据查询的过程中,当前程序进程往往只是在等待结果的返回。这就造成了进程的阻塞,对于高并发,I/O密集型的网络应用中,一方面进程长期处于等待状态,另一方面为了应付新的请求不断增加的进程。这样的情况会导致系统支持的QPS(每秒查询数)远远小于后端数据服务能够支撑的QPS,成了系统的瓶颈,而且这样的系统也很容易被慢连接攻击(客户端故意不接受或减缓接收数据的时间,加长进程的等待时间)。
解决这种问题的思路是,可以引入事件处理机制。在查询之前注册数据加载事件的响应函数,请求发出之后立即交出这个进程,而当数据返回时在出发这个事件并在预先设定好的事件响应函数中继续处理数据(有点类似AJAX的处理思路)。若按照这个思路来解决阻塞问题,我们首先要提供一套高效的异步事件调度机制,而主要用于处理浏览器端各种交互的Javascript比起其他语言,在这方面更有优势。
首先Javascript是一种函数式编程语言,函数式编程序言最重要的数学基础是λ演算(lamda calculus)-- 即函数对象可以作为其他函数对象的输入(参数)和输出(返回值)。
这一特性使得为事件指定回调函数变得方便可行。特别是Javascript还支持匿名函数。通过匿名函数的辅助,之前的代码可以变成下面的样子:
db.query("select num from person where name='aldrich'", function(phoneNum){ output(phoneNum); } );
等等,这里我们必须要提一个关键问题,异步回调的运行上下文保持。先来看一段代码何为运行保持。
JS代码:
//传统同步写法:将查询和结果打印抽象为一个方法 function main(){ var name = "aldrich"; var phoneNum = db.query("select num from person where name=" + name); output("person name:" + name + ", phoneNum :" + phoneNum ); } main();
异步待写JS代码:
//异步写法: function main(){ var name = "aldrich"; db.query("select num from person where name=" + name,function(phoneNum){ output("person name:" + name + ", phoneNum:" + phoneNum);//n秒后数据返回后执行回调 }); } main();
我们可以看到当等待了n秒数据结果返回后执行回调函数时,回调函数中却仍然使用了main函数的局部变量"name",而"name"似乎应该在n秒前已经跳出其作用域,为什么此时"name"还可以访问到呢,这就是因为Javascript的另外一个重要特性:闭包(Closures)。