当前位置: 首页 > 后端技术 > Node.js

【前端知识体系-NodeJS相关】NodeJS高频前端面试题整理

时间:2023-04-03 18:52:16 Node.js

为什么JavaScript是单线程的?#防止DOM渲染冲突;Html5中的WebWorker可以实现多线程2.什么是任务队列?#TaskQueue”是一个先进先出的数据结构,前面的事件先被主线程读取,主线程的读取过程基本是自动的,只要清空执行栈,最前面的eventonthe"TaskQueue"一位事件自动进入主线程。2.1同步和异步任务#同步任务是指在主线程中排队等待执行的任务,只有上一个任务完成了才能进行下一个任务执行;异步任务是指,对于没有进入主线程而是进入“任务队列”的任务,只有“任务队列”通知主线程可以执行一个异步任务,任务才会进入主线程2.2执行过程#所有的同步任务都在主线程上执行,形成一个执行上下文栈,除了主线程之外,还有一个“任务队列”,只要异步任务有运行结果,它就会在“任务队列”中放置一个事件,一旦“执行栈”中的所有同步任务都执行完毕,系统就会读取“任务队列”,看看里面都有哪些事件。那些对应的异步任务结束等待状态,进入执行栈。开始执行。主线程不断重复上面的第三步。什么是事件循环(EventLoop)?#主线程从“任务队列”中读取事件。这个过程是连续不断的,所以整个运行机制也称为事件循环(eventloop)。3.1定时器函数的基本使用方法比较?#setTimeout:只需将事件插入“任务队列”,主线程就会执行指定的时间,直到当前代码(执行栈)执行完毕。回调函数process.nextTick:在当前“执行栈”的末尾——下一个EventLoop之前(主线程读取“任务队列”)——回调函数被触发。(在所有异步任务触发前执行)setImmediate:在当前“任务队列”的末尾添加一个事件,即它指定的任务总是在下一个EventLoop中执行,这与setTimeout(fn,0).3.2setImmediate和setTimeout哪个回调函数先执行?#CopysetImmediate(function(){setImmediate(functionA(){console.log(1);setImmediate(functionB(){console.log(2);});});setTimeout(functiontimeout(){控制台。log('TIMEOUTFIRED');},0);});//1//TIMEOUTFIRED//2[!NOTE]上面代码中setImmediate和setTimeout封装在一个setImmediate中,其运行结果总是1--TIMEOUTFIRED--2,此时必须在超时前触发函数A。至于2在TIMEOUTFIRED之后(即超时后触发函数B),是因为setImmediate总是把事件注册到下一轮EventLoop,所以函数A和timeout在同一轮Loop中执行,而函数B在下一轮Round循环执行。3.3process.nextTick和setImmediate有什么区别?#多个process.nextTick语句总是在当前“执行栈”执行一次,多个setImmediate可能需要多次循环执行。说说NodeJS的运行机制?#V8引擎解析JavaScript脚本。解析后的代码调用NodeAPI。libuv库负责执行NodeAPI。它将不同的任务分配给不同的线程,形成一个EventLoop(事件循环),并将任务的执行结果以异步的方式返回给V8引擎。然后V8引擎将结果返回给用户。Node如何创建线程以及区别?#5.1Node的单线程#Node.js以单线程模式运行,但是它使用事件驱动来处理并发,这有助于我们在多核cpu系统上创建多个子进程来提高性能。每个子进程总是有三个流对象:child.stdin、child.stdout和child.stderr。它们可能共享父进程的stdio流,也可能是单独的转向流对象。Node提供child_process模块??创建子进程5.2创建进程的方法#exec-child_process.exec使用子进程执行命令,缓存子进程的输出,并将子进程的输出作为回调返回函数参数一次。exec方法从子进程返回一个完整的缓冲区。默认情况下,此缓冲区的大小应为200k。如果子进程返回的数据大小超过200k,程序就会崩溃并显示错误信息“Error:maxBufferexceeded”。您可以通过在exec选项中设置更大的缓冲区大小来解决此问题,但您不应该这样做,因为exec并非设计用于返回大量数据。spawn-child_process.spawn使用指定的命令行参数创建一个新进程。spawn将返回一个带有stdout和stderr流的对象。可以通过stdout流读取子进程返回给Node.js的数据。标准输出具有“数据”、“结束”和普通流具有的事件。当你想让子进程返回大量数据给Node时,比如图片处理、读取二进制数据等,最好使用spawn方法。fork-child_process.fork是spawn()的一种特殊形式,用于在子进程中运行的模块,例如fork('./son.js')等同于spawn('node',['./son.js']).与spawn方法不同,fork会在父进程和子进程之间建立一个通信通道,用于进程间的通信。5.3实例分析#5.3.1exec#Copyrequire('child_process').exec('dir',{encoding:'utf-8'},function(err,stdout,stderr){if(err){console.log(error.stack);console.log('Errorcode:'+error.code);console.log('Signalreceived:'+error.signal);}//console.log(err,stdout,stderr);console.log('data:'+stdout);}).on('exit',function(code){console.log('子进程已退出,退出码'+code);});5.3.2spawn#Copyvarchild_process=require('child_process');varspawnObj=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('closecode:'+code);}spawnObj.on('exit',(code)=>{console.log('exitcode:'+code);fs.close(fd,function(err){if(err){console.error(err);}});});5.3.3fork#分为“父进程”(parent.js)和“child.js”(child.js)在命令行执行时要切换到上述文件的目录下,否则会找不到子进程。parent.jsCopyconsole.log('parentpid:'+process.pid);varfork=require('child_process').fork;//fork方法返回子进程varchild=fork('./child.js');console.log('fork返回pid:'+child.pid);child.on('message',function(msg){console.log('parentgetmessage:'+JSON.stringify(msg));});child.send({key:'parentvalue'});child.jsCopyconsole.log('childpid:'+process.pid);process.on('message',function(msg){console.log('childgetmessage:'+JSON.stringify(msg));});process.send({key:'childvalue'});介绍一下express或者koa框架的基本架构?#Express是Node.js的一个基础框架,主要基于Connect中间件,封装了路由(需要配合bodyParser),视图处理等功能,拥有大量用户,缺点是回调方法。Koa是一个中间件框架,比Express更精简,并使用了node的新特性。它提供的是一个架子,几乎所有的功能都需要第三方中间件来完成,比如koa-router、koa-view等。[!NOTE]Koa使用co作为底层运行框架,利用了特性Generator实现“无回调”异步处理6.1路由处理#6.1.1Express#[!NOTE]使用express.Router类创建可安装的模块化Routehandler。一个Router实例是一个完整的中间件和路由系统,下面的例子创建一个路由器作为一个模块,在其中加载中间件,定义一些路由,然后将其安装到主应用程序的路径中。复制varexpress=require('express');varrouter=express.Router();router.use(functiontimeLog(req,res,next){console.log('Time:',Date.now());next();});//定义主页路由router.get('/',function(req,res){res.send('小鸟主页');});//定义关于路由router.get('/about',function(req,res){res.send('Aboutbirds');});module.exports=router;接下来,在应用程序中加载路由器模块:复制varroutes=require('./route');...app.use('/route',routes);6.1.2Koa#路由处理Express是自己集成的,Koa需要引入中间件Copyvarkoa=require('koa')varroute=require('koa-route')//中间件varapp=koa()app.use(route.get('/',function*(){this.body='HelloWorld'}))6.2HTTPRequest#[!NOTE]Both框架封装了HTTP请求对象。不同的是,Koav1使用的是这个,而不是Express的req和res。6.2.1Express#Copyvarapp=require('express')()app.get('/room/:id',function(req,res){console.log(req.params)})//获取POST数据需要body-parser中间件varbodyParser=require('body-parser')app.use(bodyParser.json())app.post('/sendgift',function(req,res){console.log(req.body)})6.2.2Koa#Copyvarapp=require('koa')()varroute=require('koa-route')app.use(route.get('/room/:id',function*(){console.log(this.req.query)}))//获取POST数据需要co-body中间件varparse=require('co-body')app.use(route.post('/sendgift',function*(){varpost=yieldparse(this.request)console.log(post)}))6.3区别#6.3.1异步流程控制#Express使用回调处理异步,Koav1使用generator,Koav2使用async/等待。6.3.2错误处理#Express使用回调来捕获异常,无法捕获深层次的异常。koa使用trycatch,可以更好的解决异常捕获。Copy//Express回调app.use(function(err,req,res,next){console.error(err.stack)res.status(500).send('Somethingbroke!')})//Koageneratorapp.use(function*(next){try{yieldnext}catch(err){this.status=err.status||500this.body={message:err.message}this.app.emit('error',err,this)}})//Koaasync/awaitapp.use(async(ctx,next)=>{try{awaitnext()}catch(err){ctx.status=err.status||500ctx.body={message:err.message}ctx.app.emit('error',err,this)}})6.3.3中间件处理#App.use在Express中就是往中间件数组中插入新的中间件,中间件处理method是线性的,next之后继续寻找下一个中间件。一个请求进来,经过一系列的中间件处理后才响应给用户,一目了然。缺点:基于回调结合业务逻辑,当业务逻辑复杂时,嵌套过多,难以捕获异常。Koa的中间件处理方式是洋葱模型。koa处理完中间件就会回来,这样就给了我们更大的操作空间。复制constKoa=require('koa');constapp=newKoa();//x-response-timeapp.use(async(ctx,next)=>{conststart=Date.now();awaitnext();constms=Date.now()-start;ctx.set('X-Response-Time',`${ms}ms`);});//loggerapp.use(async(ctx,next)=>{conststart=Date.now();awaitnext();constms=Date.now()-start;console.log(`${ctx.method}${ctx.url}-${ms}`);});//响应应用.use(asyncctx=>{ctx.body='HelloWorld';});[!NOTE]当koa处理中间件遇到awaitnext()时,会暂停当前中间件继续处理下一个中间件,最后返回继续处理剩下的任务