• node.js学习笔记(三)——事件循环


      要理解事件循环,首先要理解事件驱动编程(Event Driven Programming)。它出现在1960年。如今,事件驱动编程在UI编程中大量使用。JavaScript的一个主要用途是与DOM交互,所以使用基于事件的API是很自然的。简单地定义:事件驱动编程通过事件或状态的变化来进行应用程序的流程控制。一般通过事件监听实现,一旦事件被检测到(即状态改变)则调用相应的回调函数。听起来很熟悉?其实这就是node.js事件循环的基本工作原理。如果你熟悉客户端JavaScript的开发,想一想那些.on*()方法,如element.onclick(),他们用来与DOM元素相结合,传递用户交互。这个工作模式允许在单个实例上触发多个事件。Node.js通过EventEmitter(事件发生器)触发这种模式,如在服务器端的Socket和 “http”模块中,可以从一个单一实例触发一种或一种以上的状态改变。

      js是单线程,对于阻塞操作,js会封装参数和回调函数,交给底层去处理,也就是io线程池。线程池处理完毕会放到一个类似队列里面,然后node的事件循环线程会去获取这个队列的数据,执行回调。也就是说使用事件驱动模型的node.js,在当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)。

       在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

      如上图所示,每个异步函数执行结束后,都会在事件队列中追加一个事件(同时保存一些必要参数)。事件轮询下一次循环便可取出事件,然后会调用异步方法对应的回调函数(参数)。这样一来,nodejs便能保证开发者编写的每行代码(每个回调)均在主线程中执行。注意这里有一个问题,如果开发者在回调函数中调用了阻塞方法,那么整个事件轮询就会阻塞,事件队列中的事件得不到及时处理。正因为这样,node.js中的一些库方法均是异步的,也提倡用户调用异步方法。

      node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类(EventEmitter类会在下一笔记做分析)来绑定和监听事件,如下实例:

    // 引入 events 模块
    var events = require('events');
    // 创建 eventEmitter 对象
    var eventEmitter = new events.EventEmitter();

      绑定事件处理程序:

    // 绑定事件及事件的处理程序
    eventEmitter.on('eventName', eventHandler);

      触发事件:

    // 触发事件
    eventEmitter.emit('eventName');

      接下来就通过一个实例来实操一下。毕竟说得再多还不如动手试试。

      创建 demo3.js 文件,代码如下所示:

    // 引入 events 模块
    var events = require('events');
    // 创建 eventEmitter 对象 var eventEmitter = new events.EventEmitter(); // 创建事件处理程序 var connectHandler = function connected() { console.log('连接成功。'); // 触发 data_received 事件 eventEmitter.emit('data_received'); } // 绑定 connection 事件处理程序 eventEmitter.on('connection', connectHandler); // 使用匿名函数绑定 data_received 事件 eventEmitter.on('data_received', function(){ console.log('数据接收成功。'); }); // 触发 connection 事件 eventEmitter.emit('connection'); console.log("程序执行完毕。");

      执行node命令查看效果:

      

      在 node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

      下面我们再来看一个栗子。先创建一个txt文本文件,比如demo3_1.txt,文本内容如下:

    this is a demo!

      再创建一个node脚本,比如叫demo3_1.js,代码如下:

    //引入文件操作系统模块
    var fs = require("fs");
    //异步读取文件内容
    fs.readFile('demo3_1.txt', function (err, data) {
           //如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。
           //如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。
        if (err){
              console.log(err.stack);
              return;
        }
        console.log(data.toString());
    });
    
    console.log("程序执行完毕");  

      执行node命令查看效果:

      

      可以看到,脚本读取文件内容成功,所以不会报错。我们可以试着让它报错,比如把txt文件删除了,然后再来看看效果。

      由于文件 demo3_1.txt 不存在,所以输出了错误信息。

      上面可以看到,fs.readFile()方法的最后一个参数就是回调函数,而回调函数里面第一个参数就是回调函数接受的错误参数。

      关于node.js的事件循环机制,我还只是了解了点皮毛,如果有大神愿意指导,鄙人很愿意聆听(*^-^*)

  • 相关阅读:
    关于父子页面的交互
    Spring在代码中获取bean的几种方式(转:http://www.dexcoder.com/selfly/article/326)
    关于Zookeeper
    Java 生成pdf表格文档
    Spring 框架中Http请求处理流程
    关于redis
    Xstream 解析xml文件内容
    ArrayBlockingQueue 和LinkedBlockQueue
    spring IOC
    springboot启动过程
  • 原文地址:https://www.cnblogs.com/slly/p/6474258.html
Copyright © 2020-2023  润新知