• 技术文章 >web前端 >js教程

    Node.js的内置模块 event,利用它怎么实现发布订阅模式

    青灯夜游青灯夜游2022-09-23 20:11:19转载129
    对于发布订阅模式想必大家并不陌生,它在异步交互中具有很大的作用,能够使我们的代码结构更加清晰易读,便于维护。

    大前端零基础入门到就业:进入学习

    node中我们可以使用 内置模块event 来实现发布订阅模式,这篇文章我们将深入去学习event并演示它在我们实际开发中的作用,让我们开始吧!【相关教程推荐:nodejs视频教程

    一、初步使用

    引入event内置模块

    // 引入内置模块event
    const EventEmitter = require("events");

    创建event对象

    event内置模块本质是一个构造函数,我们需要通过new操作符去调用它

    // 创建event对象
    const event = new EventEmitter();

    监听事件

    使用event对象上的on函数来定义一个监听事件,语法为:event.on(事件名,事件处理函数)

    // 监听run事件
    event.on("run", (data) => {
        console.log("run事件运行,参数为:", data);
    });

    触发事件

    使用event对象上的emit函数来触发监听的事件,语法为:event.emit(需要触发的事件名,需要给事件处理函数传递的参数)

    // 触发run事件
    event.emit("run", "111111");

    完整代码

    // 引入内置模块event
    const EventEmitter = require("events");
    // 创建event对象
    const event = new EventEmitter();
    
    // 监听run事件
    event.on("run", (data) => {
        console.log("run运行,参数为:", data);
    });
    
    // 触发run事件
    event.emit("run", "111111");

    运行结果:

    1.png

    ❗️ 事件重复监听的问题

    ==注意:当同一事件被监听多次时,触发事件时会同时触发这个事件的所有事件处理函数==

    2.png

    二、应用

    在上一节Node.js | 搭建后端服务器(含内置模块 http | url | querystring 的使用)中有一个使用node模拟get请求(转发跨域数据)的案例:

    const http = require("http");
    const https = require("https");
    // http和https的区别仅在于一个是http协议一个是https协议
    const url = require("url");
    
    const server = http.createServer();
    
    server.on("request", (req, res) => {
        const urlObj = url.parse(req.url, true);
    
        res.writeHead(200, {
            "content-type": "application/json;charset=utf-8",
            "Access-Control-Allow-Origin": "http://127.0.0.1:5500",
        });
    
        switch (urlObj.pathname) {
            case "/api/maoyan":
                // 我们定义的httpget方法:使node充当客户端去猫眼的接口获取数据
                httpget((data) => res.end(data)); // 注意这里
                break;
    
            default:
                res.end("404");
                break;
        }
    });
    
    server.listen(3000, () => {
        console.log("服务器启动啦!");
    });
    
    function httpget(cb) {
        // 定义一个存放数据的变量
        let data = "";
        // 因为猫眼的接口是https协议的,所以我们需要引入https
        // http和https都具有一个get方法能够发起get请求,区别是一个是http协议,一个是https协议
        // http get方法第一个参数为接口地址,第二个参数为回调函数
        https.get(
            "https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4",
            (res) => {
                // http get方法获取的数据是一点点返回的,并不是直接返回全部
                // 监听data,当有数据返回时就会被调用
                res.on("data", (chunk) => {
                    // 收集数据
                    data += chunk;
                });
                // 监听end,数据返回完毕后调用
                res.on("end", () => {
                    cb(data); // 注意这里
                });
            }
        );
    }

    注意上面代码的第19行和第49行:

    httpget((data) => res.end(data)); // 注意这里
    cb(data); // 注意这里

    这个例子中,我们是通过在httpget函数中传入一个回调函数来接收httpget函数获取到的数据,这种写法实际是没有问题的,在开发中也常常进行使用。

    但在一些情况下,特别是函数多层嵌套调用时(如下面的例子),这种写法就显得不够优雅,因为它的代码结构不是很清晰,不能很直观的看懂其逻辑:

    function user() {
        getUser((data) => {
            console.log(data);
        });
    }
    
    function getUser(cb) {
        // ....
        const id = 1;
        getUserInfo(cb, id);
    }
    
    function getUserInfo(cb, id) {
        // ....
        const name = id + "Ailjx";
        cb(name);
    }

    让我们使用内置模块event去改造一下上面node模拟get请求(转发跨域数据)的案例:

    const http = require("http");
    const https = require("https");
    const url = require("url");
    const EventEmitter = require("events");
    const server = http.createServer();
    
    // 存放event对象
    let event = "";
    
    server.on("request", (req, res) => {
        const urlObj = url.parse(req.url, true);
    
        res.writeHead(200, {
            "content-type": "application/json;charset=utf-8",
            "Access-Control-Allow-Origin": "http://127.0.0.1:5500",
        });
    
        switch (urlObj.pathname) {
            case "/api/maoyan":
                event = new EventEmitter(); // 注意该位置
                // 监听事件
                event.on("resEnd", (data) => {
                    res.end(data);
                });
                httpget();
                break;
    
            default:
                res.end("404");
                break;
        }
    });
    
    server.listen(3000, () => {
        console.log("服务器启动啦!");
    });
    
    function httpget() {
        let data = "";
        https.get(
            "https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4",
            (res) => {
                res.on("data", (chunk) => {
                    data += chunk;
                });
                res.on("end", () => {
                    // 触发事件并传递数据
                    event.emit("resEnd", data);
                });
            }
        );
    }

    运行并调用/api/maoyan接口:

    3.png

    接口正常使用

    注意上边代码new EventEmitter()的位置,如果new EventEmitter()是在外部的话,相当于是只有一个全局的event对象,当我们每次调用/api/maoyan接口时,node都会监听一个新的resEnd事件,这就会导致resEnd事件被重复监听

    4.png

    所以我们才需要将创建event对象的代码new EventEmitter()写到接口的case分支里,这样当我们调用这个接口时,会创建一个新的event对象,老的event对象被弃用会被JS垃圾处理机制给处理掉,这样就不会出现resEnd事件被重复监听的问题

    更多node相关知识,请访问:nodejs 教程

    以上就是Node.js的内置模块 event,利用它怎么实现发布订阅模式的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Node.js node nodejs​
    上一篇:【整理分享】NPM 实用技巧,让你的同事刮目相看! 下一篇:没有了
    VIP课程(WEB全栈开发)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• node爬取数据实例:抓取宝可梦图鉴并生成Excel文件• 如何进行Node.js扩展开发?前置知识分享• 教你Node.js+SpreadJS从服务端生成Excel电子表格• 一文聊聊Node中的进程间通信• 探索 Node.js 源码,详解cjs 模块的加载过程• 浅析Node处理CPU密集型任务的方法
    1/1

    PHP中文网