Node异步编程范式

  • 关键词:Node.js – 进阶

一、Node异步编程范式

1.同步与异步的比较

Node是一个JavaScript运行时环境,它为JavaScript提供了一个异步I/O编程框架。

Node的指导思想:解决一类问题,就是CPU执行指令是非常快速的,但I/O操作相对而言是极其缓慢的。

假设一次客户请求分为三个阶段————执行函数A,一次I/O操作,执行函数B

image

同步式处理方式中,每个请求用一个线程(或进程)处理。一次请求处理完毕之后,线程被回收。

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的通知机制,把同步代码交给由操作系统管理的线程池运行。