nodejs笔记之:事件驱动,线程池,非阻塞,异常处理等

事件驱动

事件驱动的模型:


nodejs笔记之:事件驱动,线程池,非阻塞,异常处理等_第1张图片

事件驱动的原理:

原理总结:

Nodejs 会把所有请求和异步操作都放到一个事件队列中,用户的每一个请求就是一个事件。主线程先把普通代码执行完毕,然后会循环事件队列里的函数,如果遇到有IO的操作,nodejs会去线程池里拿出一个线程去执行IO的操作,执行完毕后再把拿到数据的回调函数,放到事件队列的尾部,继续事件循环。

线程池

线程池的概念:

Node是单线程的,这里的单线程仅仅是javascript执行在单线程中,V8引擎不支持io操作,在node中,无论是类unix还是windows平台内部完成IO的操作都有线程池。

在windows下,node实现IO异步的解决方案是iocp:调用异步方法,等待IO完成之后的通知,执行回调,用户无需考虑轮询,但是其内部仍然是线程池原理,不同之处在于这些线程池是由系统内核接受管理。
在linux下,0.9.3版本之前的node是使用libeio配合libuv实现的异步IO,在此版本之后的node自行实现了线程池来完成异步io操作。
本质上nodejs 是个多线程平台。如果有io操作,nodejs交给 中间层libuv去做,它的底层就是C或者C++来实现的,所以nodejs本质上还是个多线程平台。

IO 操作

io操作就是以流的形式,进行的操作,比如网络请求,文件读取写入。io操作也就是input和output的操作。

阻塞IO

在调用阻塞io时,应用程序需要等待io完成才能返回结果。
阻塞io的特点:调用之后一定要等到系统内核层面完成所有操作之后,调用才结束。
阻塞io造成CUP等待IO,浪费等待时间,CPU的处理能力不能得到充分利用。

非阻塞IO

为了提高性能,内核提供了非阻塞io,非阻塞io跟阻塞io的差别是调用之后会立即返回。
阻塞io完成整个获取数据的过程,而非阻塞io则不带数据直接返回,要获取数据,还要通过问价你描述符再次读取。
非阻塞io返回之后,cpu时间片可以用来处理其他事物,此时性能提升非常明显。

Nodejs中的异常处理

Node是单线程运行环境,一旦抛出异常,没有被处理,就会引起整个进程崩溃。
Node的异常处理,对于保证系统稳定运行非常重要。
Node有三种方式,处理一个错误 :
    1. throw 抛出异常。
    2. 将错误对象传递给回调函数,由回调函数负责处理错误。
    3. 使用try catch捕获异常有时候会不及时,合理的放置 trycath 去捕获,在异步内部,而不是在异步的外层去捕获,就像是下面的在回调函数内部去try catch 。

回调函数

Node采用: 将错误对象作为第一个参数传入回调函数,这样就避免了捕获错误与发生错误不在一个时间段上的问题。

回调函数的设计

对于一个函数需要定义回调函数,node统一规定:

  1. 回调函数一定作为参数的最后一个参数出现:

    Function foo(arg1,arg2,callback){}
  2. 回调函数的第一个参数默认接收错误对象,第二个参数才是真正的回调函数(便于外界获取调用的错误情况)

    // 执行一个doRead() 函数,假设有这么一个函数,主要看里面的异常处理
    doRead(obj,function(err,res){
        if(err) throw err; // err 是错误对象
        console.log(res); // res 是具体的结果或者数据
     } )
  3. 强调错误优先

    • 错误优先的回调函数,第一个参数是上一步的错误对象
    • 通过判断回调函数中的err是否为null,来检测异步操作过程是否出现错误,我们用下面的例子来举例说明:

      const fs = require('fs');
      // 定义一个读文件函数
      function readFile(path, callback) {
        fs.readFile(path, 'utf8', function (err, data) {
          if (err) {
            return callback(err, null);
          }
          callback(null, '输出的数据为:' + data);
        });
      }
      
      readFile('./data/index.html', function (err, data) {
        if (err) {
          throw err;
        }
        console.log(data);
      });
      

总结:

  1. Node规定,如果某个函数需要作为参数,则回调函数是最后一个参数,另外,回调函数本身的第一个参数,约定为上一步传入的错误对象
  2. 传统的错误捕捉机制try catch,尽量不要使用,在异步操作中容易出错。
  3. 一旦异步操作发生错误,就把错误对象传递到回调函数,如果没有发生错误,回调函数的第一个参数就是null,如果不是null,就肯定出错了。

你可能感兴趣的:(nodejs笔记之:事件驱动,线程池,非阻塞,异常处理等)