Node异步编程范式
- 关键词:Node.js – 进阶
一、Node异步编程范式
1.同步与异步的比较
Node是一个JavaScript运行时环境,它为JavaScript提供了一个异步I/O编程框架。
Node的指导思想:解决一类问题,就是CPU执行指令是非常快速的,但I/O操作相对而言是极其缓慢的。
假设一次客户请求分为三个阶段————执行函数A,一次I/O操作,执行函数B
同步式处理方式中,每个请求用一个线程(或进程)处理。一次请求处理完毕之后,线程被回收。
Node一个主线程解决了所有的问题。Node是“站在巨人的肩上”,这个巨人就是V8引擎。
高阶函数与闭包
如果一个函数以一个或多个函数作为参数,或者返回一个函数,那么称此函数为高级函数。高阶函数执行后返回的函数,或者接受的函数参数,则称为闭包。
闭包的最大特点就是:引用了它之外的变量,这些参数既不是全局的,也不是参数和局部的,而是作为闭包执行时的上下文环境存在。
function wrapper(price){
var freeVal = price;
function closure(delta){
return freeVal * delta;
}
return closure;
}
2.Node异步的实现
因为异步带来的返回结果次序的不确定性,是异步编程框架需要解决的一个问题。当主线程得到通知后,如何识别异步请求是谁发起的,以及得到数据之后下一步该做什么。
这里恰恰就需要闭包的特性对接执行流程。因为闭包保留了异步调用发起时的上下文信息。
如果来了1000个并发请求,在异步方式下,不需要开多余的线程,所有请求均由主线程承担。一旦遇到耗时的I/O请求,以异步方式发起调用。一次异步调用同时也会创建一个闭包进去,操作系统执行具体的I/O操作,将结果放入指定缓存。
2.1 HTTP请求-完全异步
"use strict"; //表示以严格模式运行
var http = require('http');
var options = {
host: '#',
port: 80,
path: '/',
method: 'get',
headers: {
'Content-Type':'application/json'
}
};
var req = http.request(options);
//使用了ES6标准的箭头函数(小到解构赋值,大到原生支持Promise对象和生成器),更加简洁
req.once('response', (res)=>{
var result = '';
res.on('data', function(chunk){
result += chunk.toString();
});
res.on('end', function(){
console.log(result);
});
});
req.on('error', (e)=>{
console.error(e.message);
};
req.end();
- 1.连接阶段
当调用http.request函数发起请求之后,Node会建立与服务器的socket连接,而socket连接需要IP地址和端口号,所有中间还有一个DNS解析过程。
待更新。。。
- 2.请求与应答阶段
待更新。。。
- 3.结束阶段
待更新。。。
2.2 本地磁盘I/O-多线程模拟
Node的异步调用,其背后的机制是Linux的Epoll。在Windows下,则是采用完成端口(IOCP)。
但对于本地磁盘的I/O,Node使用了不同层策略。
(1)本地磁盘读写要比网络请求快得多;
(2)磁盘文件按块访问,操作系统的缓存机制使得顺序读写文件的效率极高;
(3)相对于同步的读写,完全异步的处理流程复杂得多。
Linux下Node在启动时,会维护一个线程池,Node使用这个线程池的线程,同步地读写文件;而在Windows下,则是利用IOCP的通知机制,把同步代码交给由操作系统管理的线程池运行。