NodeJS框架

简介: JavaScript为避免DOM渲染冲突采用单线程模型,异步任务通过任务队列和事件循环(EventLoop)实现。同步任务在执行栈中执行,异步任务进入任务队列等待回调。Node.js利用libuv进行事件驱动,支持子进程(exec、spawn、fork)提升性能。Express基于中间件线性处理,Koa采用洋葱模型,支持async/await,错误处理更优。
  1. 为什么JavaScript是单线程?
    ● 防止DOM渲染冲突的问题;
    ● Html5中的Web Worker可以实现多线程
    2.什么是任务队列?
    任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。
    2.1 同步和异步任务
  2. 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
  3. 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
    2.2 执行流程
  4. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  5. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  6. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  7. 主线程不断重复上面的第三步。
  8. 什么是事件循环(EventLoop)?
    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
    3.1 定时器函数的基本使用方法对比?
    ● setTimeout : 只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数
    ● process.nextTick : 在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。(所有的异步任务被触发之前执行)
    ● setImmediate:在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。
    3.2 setImmediate和setTimeout 哪个回调函数先执行呢?
    setImmediate(function (){
    setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
    });

setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);
});
// 1
// TIMEOUT FIRED
// 2
Copy
Note
上面代码中,setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果总是1--TIMEOUT FIRED--2,这时函数A一定在timeout前面触发。至于2排在TIMEOUT FIRED的后面(即函数B在timeout后面触发),是因为setImmediate总是将事件注册到下一轮Event Loop,所以函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。
3.3 process.nextTick和setImmediate的区别?
多个process.nextTick语句总是在当前"执行栈"一次执行完,多个setImmediate可能则需要多次loop才能执行完。

  1. 说一下NodeJS的运行机制?
  2. V8引擎解析JavaScript脚本。
  3. 解析后的代码,调用Node API。
  4. libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
  5. V8引擎再将结果返回给用户。
  6. Node创建线程的方法和区别?
    5.1 Node的单线程
  7. Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。
  8. 每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。
  9. Node 提供了 child_process 模块来创建子进程
    5.2 创建进程的方法
  10. exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式一次性返回。exec方法会从子进程中返回一个完整的buffer。默认情况下,这个buffer的大小应该是200k。如果子进程返回的数据大小超过了200k,程序将会崩溃,同时显示错误信息“Error:maxBuffer exceeded”。你可以通过在exec的可选项中设置一个更大的buffer体积来解决这个问题,但是你不应该这样做,因为exec本来就不是用来返回很多数据的方法。
  11. spawn - child_process.spawn 使用指定的命令行参数创建新进程。spawn 会返回一个带有stdout和stderr流的对象。你可以通过stdout流来读取子进程返回给Node.js的数据。stdout拥有’data’,’end’以及一般流所具有的事件。当你想要子进程返回大量数据给Node时,比如说图像处理,读取二进制数据等等,你最好使用spawn方法。
  12. fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 相当于 spawn(‘node’, [‘./son.js’]) 。与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。
    5.3 实例分析
    5.3.1 exec
    require('child_process').exec('dir', {encoding: ‘utf-8’}, function(err, stdout, stderr) {
    if (err) {
       console.log(error.stack);
       console.log('Error code: ' + error.code);
       console.log('Signal received: ' + error.signal);
    
    }
    //console.log(err, stdout, stderr);
    console.log('data : ' + stdout);
    }).on('exit', function (code) {
    console.log('子进程已退出, 退出码 ' + code);
    });
    Copy
    5.3.2 spawn
    var child_process = require('child_process');
    var spawnObj = child_process.spawn('ping', ['127.0.0.1'], {encoding: 'utf-8'});
    spawnObj.stdout.on('data', function(chunk) {
    console.log(chunk.toString());
    });
    spawnObj.stderr.on('data', (data) => {
    console.log(data);
    });
    spawnObj.on('close', function(code) {
    console.log('close code : ' + code);
    }
    spawnObj.on('exit', (code) => {
    console.log('exit code : ' + code);
    fs.close(fd, function(err) {
       if(err) {
           console.error(err);
       }
    
    });
    });
    Copy
    5.3.3 fork
    分为 “父进程”(parent.js) 和”子进程”(child.js)。在命令行执行的时候要切换到上述文件的目录中,否则会找不到子进程。
    parent.js
    console.log('parent pid: ' + process.pid);
    var fork = require('child_process').fork;
    //fork方法返回的是子进程
    var child = fork('./child.js');
    console.log('fork return pid: ' + child.pid);
    child.on('message', function(msg){
    console.log('parent get message: ' + JSON.stringify(msg));
    });
    child.send({key: 'parent value'});
    Copy
    child.js
    console.log('child pid: ' + process.pid);
    process.on('message', function(msg){
    console.log('child get message: ' + JSON.stringify(msg));
    });
    process.send({key: 'child value'});
    Copy
  13. 介绍一下express或koa框架的基本架构?
  14. Express 是一个Node.js的基础框架,主要基于 Connect 中间件,并且自身封装了路由(需要配合bodyParser)、视图处理等功能,使用人数众多,弊端是callback回调方式。
  15. Koa 是一个比Express更精简,使用node新特性的中间件框架。其提供的是一个架子,而几乎所有的功能都需要由第三方中间件完成,比如koa-router, koa-view等。
    Note
    Koa 利用 co 作为底层运行框架,利用 Generator 的特性,实现“无回调”的异步处理
    6.1 处理路由
    6.1.1 Express
    Note
    使用 express.Router 类来创建可安装的模块化路由处理程序。Router 实例是完整的中间件和路由系统,以下示例将路由器创建为模块,在其中装入中间件,定义一些路由,然后安装在主应用程序的路径中。
    var express = require('express');
    var router = express.Router();
    router.use(function timeLog(req, res, next) {
    console.log('Time: ', Date.now());
    next();
    });
    // define the home page route
    router.get('/', function(req, res) {
    res.send('Birds home page');
    });
    // define the about route
    router.get('/about', function(req, res) {
    res.send('About birds');
    });

    module.exports = router;
    Copy
    接着,在应用程序中装入路由器模块:
    var routes = require('./route');
    ...
    app.use('/route', routes);
    Copy
    6.1.2 Koa
    路由处理 Express 是自身集成的,而 Koa 需要引入中间件
    var koa = require('koa')
    var route = require('koa-route') //中间件
    var app = koa()
    app.use(route.get('/', function *(){
    this.body = 'Hello World'
    }))
    6.2 HTTP Request
    Note
    两个框架都封装了HTTP Request对象,有一点不同是 Koa v1 使用 this 取代 Express 的 req、res。
    6.2.1 Express
    var app = require('express')()
    app.get('/room/:id', function (req, res) {
    console.log(req.params)
    })
    // 获取POST数据需要 body-parser 中间件
    var bodyParser = require('body-parser')
    app.use(bodyParser.json())
    app.post('/sendgift', function (req, res) {
    console.log(req.body)
    })
    6.2.2 Koa
    var app = require('koa')()
    var route = require('koa-route')

    app.use(route.get('/room/:id', function *() {
    console.log(this.req.query)
    }))

    // 获取POST数据需要 co-body 中间件
    var parse = require('co-body')
    app.use(route.post('/sendgift', function () {
    var post = yield parse(this.request)
    console.log(post)
    }))
    6.3 区别
    6.3.1 异步流程控制
    Express 采用 callback 来处理异步,Koa v1 采用 generator,Koa v2 采用 async/await。
    6.3.2 错误处理
    Express 使用 callback 捕获异常,对于深层次的异常捕获不了, Koa 使用 try catch,能更好地解决异常捕获。
    // Express callback
    app.use(function (err, req, res, next) {
    console.error(err.stack)
    res.status(500).send('Something broke!')
    })
    // Koa generator
    app.use(function
    (next) {
    try {
    yield next
    } catch (err) {
    this.status = err.status || 500
    this.body = { message: err.message }
    this.app.emit('error', err, this)
    }
    })
    // Koa async/await
    app.use(async (ctx, next) => {
    try {
    await next()
    } catch (err) {
    ctx.status = err.status || 500
    ctx.body = { message: err.message }
    ctx.app.emit('error', err, this)
    }
    })
    6.3.3 中间件处理

  16. Express中app.use就是往中间件数组中塞入新的中间件,中间件处理方式是线性的,next过后继续寻找下一个中间件。 一个请求进来经过一系列中间件处理后再响应给用户,清晰明了。
  17. 缺点:基于 callback 组合业务逻辑,业务逻辑复杂时嵌套过多,异常捕获困难。
  18. Koa的中间件处理方式是一个洋葱模型,koa处理完中间件后还会回来走一趟,这就给了我们更加大的操作空间。
    const Koa = require('koa');
    const app = new Koa();

    // x-response-time
    app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    ctx.set('X-Response-Time', ${ms}ms);
    });

    // logger
    app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    console.log(${ctx.method} ${ctx.url} - ${ms});
    });

    // response
    app.use(async ctx => {
    ctx.body = 'Hello World';
    });
    Note
    当koa处理中间件遇到await next()的时候会暂停当前中间件进而处理下一个中间件,最后再回过头来继续处理剩下的任务

相关文章
|
15天前
|
人工智能 自然语言处理 网络协议
2026年阿里云OpenClaw(Clawdbot)部署新手教程 零基础15分钟上手专属AI助手
2026年,AI自动化工具迎来全民普及,OpenClaw凭借“自然语言指令+任务自动化”的核心优势,成为个人与轻量团队搭建专属AI助手的首选。其前身为Clawdbot、Moltbot,历经版本迭代后,统一命名为OpenClaw,功能更完善、适配性更强,无需用户掌握编程技能,即可通过日常口语化指令,完成文档处理、日程管理、跨工具协同等各类重复性工作,被用户称为“私人AI员工”。
222 0
|
1月前
|
算法 搜索推荐 Serverless
为什么 ES 的搜索结果只到 10,000?强制“数清楚”的代价有多大
Elasticsearch 7.x后默认返回10,000总数,实为Block-Max WAND算法的性能优化——跳过低分文档块以提升查询速度。强行开启`track_total_hits:true`将禁用该优化,导致CPU飙升、延迟激增。本文深入Lucene底层,解析其原理、陷阱与治理方案。
337 1
|
1月前
|
弹性计算 关系型数据库 MySQL
2026年阿里云数据库使用全指南:购买、配置、连接与核心特性解析
阿里云数据库涵盖关系型(RDS、PolarDB)、NoSQL(Redis、MongoDB)等多类产品,支持 MySQL、SQL Server、PostgreSQL 等主流引擎,凭借高可用、易运维特性,适配从个人开发到企业级业务的多元需求。本文结合官方标准流程与实践经验,从购买选型、实例配置、连接使用到性能优化展开解析,帮助用户高效上手阿里云数据库服务。
|
2月前
|
人工智能 自然语言处理 安全
中小微企业 AI 客服系统建设费用:高性价比选择指南
瓴羊Quick Service是阿里云旗下智能客服产品,专为中小微企业设计,支持全渠道接入、7×24小时AI应答,准确率达93%,年费仅1万元起,助力企业低成本实现高效服务升级。
|
2月前
|
存储 JavaScript 前端开发
XSS攻击
XSS(跨站脚本攻击)是攻击者通过网站漏洞注入恶意脚本,用户访问时执行,窃取数据、Cookie或劫持会话。主要分反射型和存储型,危害大。防御措施包括输入转义、白名单过滤及CSP内容安全策略,有效防止脚本注入。
|
2月前
|
弹性计算 安全 Linux
阿里云免费云服务器及学生云服务器申请教程指南
阿里云继续推出免费学生云服务器与免费试用云服务器,其中学生云服务器最长可免费7个月(1个月首次领用+6个月免费续领),免费试用云服务器分为个人免费云服务器和企业免费云服务器,最长免费试用时长是3个月。下面小编来介绍一下阿里云免费云服务器及学生云服务器的申请教程。
|
4月前
|
监控 安全 数据挖掘
安徽京准分享:安防监控系统精准NTP时钟同步应用方案
安防监控系统精准NTP时钟同步方案,通过北斗/GPS双模授时,局域网部署NTP服务器,实现毫秒级时间统一,提升事件追溯、数据联动与应急响应效率,筑牢系统协同与证据有效性基石。(238字)
|
7月前
|
JSON 监控 API
虾皮商品 API,开发者详解与使用指南
虾皮商品API支持开发者获取商品详情及列表,适用于电商导购、价格监控、库存管理等场景。提供商品信息查询、店铺管理、数据分析等功能,助力商家优化运营策略。
|
数据可视化 JavaScript 前端开发
Matplotlib动画制作:让数据生动起来!
Matplotlib动画制作:让数据生动起来!
337 0
|
运维 测试技术 Serverless
Koupleless 2024 年度报告 & 2025 规划展望
2024年,Koupleless 社区在研发框架、模块化兼容性治理及运维调度等方面取得显著进展,共合并608个PR,发布10次主版本,新增20位贡献者。社区企业接入数达45家,涵盖快手、涂鸦、民生银行等。展望2025年,将继续完善模块化工具链,并建设DevOps平台和Serverless能力,助力更多企业实现降本增效。 本文4389字,预计阅读8分钟。

热门文章

最新文章