Nodejs进阶学习
javascript里程碑事件
1995年javascript诞生
1997年6月,ECMAScript 1.0
1998年6月,ECMAScript 2.0
1999年,ActiveX问世,各大厂商实现自己XMLHttpRequest
1999年12月,ECMAScript 3.0
2005年2月,ajax问世
2006年1月,jquery问世
2008年12月,谷歌浏览器问世(V8)
2009年6月,nodejs问世
2009年12月, ECMAScript 5.0
2010年,npm问世
2015年6月,ECMAScript 6.0
2010年,npm问世
写法规范
es6规范
1 | // es6规范 |
commonjs规范
1 | // commonjs规范 nodejs(模块缓存) |
异步解决方案-Promise(或者aysnc/await)
1 | const myPromise = new Promise((resolve, reject)=> { |
事件循环
浏览器事件循环
a.JS线程读取并执行JS代码
b.执行JS代码的过程中,指定异步的操作给对应的线程处理
c.异步线程处理完毕之后,将对应的回调函数推入任务队列(多个)
d.JS线程执行完毕之后,查询任务队列,取一个任务推入JS线程运行
e: 重复b-d
1 | console.log(1); |
执行流程:
- 查询宏任务队列(把脚本执行当作一个宏任务来看待),取一个执行
- 查询微任务队列,全部执行完毕,包括当前微任务队列执行时产生的新的微任务

宏任务队列
Ajax请求,绑定事件的回调函数,定时器的回调函数…
微任务队列
promsie, Object.observe, MutationObserver
总结:
- 先执行宏任务(script全局代码)
- 在执行宏任务的过程中(主进程),
2.1 遇到宏任务(setTimeout/setInterval),把该宏任务push到宏任务队列
2.2 遇到微任务(new Promise.then)把该微任务push到微任务队列
2.3 执行完该宏任务后,在执行该宏任务产生的微任务队列 - 重复执行第二步骤,直至宏任务队列和微任务队列执行完毕,退出javascript脚本执行
浏览器的构成

nodejs事件循环

定时器阶段:setTimeout/setInterval
等待回调阶段:执行某些系统操作的回调,TCP错误处理
闲置阶段、准备阶段:只在内部使用
轮询阶段:I/O 回调
检查阶段:setImmediate
关闭回调阶段:关闭类的回调,socket.on(‘close’,…)
ps: 在执行每个阶段任务队列之前,都会清空process.nextTick队列和Promise队列,process.nextTick队列执行优先级更高
在事件循环poll阶段才会把I/O回调放到等待回调阶段
timers优先级最高
定时器阶段(timers):
- 执行优先级更高(可以理解为,用户已经等了定时器执行很久了,所以优先执行)
- 定时器有可能被轮询阶段正在执行的回调阻塞导致延迟(首次轮询不会有阻塞) ps: 用代码模拟一个场景
待定回调(pedding callbacks)
idle, prepare: 仅内部系统使用
轮询(poll)
检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
1 | console.log(1); |
1 | 2. Node中的事件循环 |
案例
1 | // p1 |
nodejs事件循环6个事件


nodejs的构成

如何证明nodejs是单线程
1 | setTimeout(() => { |
启动web服务
1 | const express = require('express'); |
JSON.stringify和JSON.parse执行耗时
1 | var obj = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }; |
promise
promise抛出异常或者reject错误,如果promise.then定义了异常处理函数,后面.then会走到成功回调函数
1 | const flag = false; |
nextTick循环调用,导致性能问题
1 | // nextTick每个阶段执行之前都要先清空nextTick队列 |
nodejs循环依赖
1 | // a.js |
???
1 | // process |
原生node启动web服务
1 | const http = require("http"); |
记录同步/异步错误日志
1 | const { Console } = require('console'); |
commonjs注入模块的变量
1 | // module.js |
执行优先级对比(Promise,setImmediate,process.nextTick,setTimeout)
1 | Promise.resolve().then(() => console.log('p1')); |
对比timeout和immediate执行时机
1 | // 比较 setTimeout 和 setImmediate 在不同情况下的优先级 |

总结:两种输出结果,主要原因是文件读取时间加while循环条件是否在1000毫秒内,第一次读取文件没有缓存,超过1000毫秒,所以先输出’timer’. 第二次执行fs读取缓存文件,小于1000毫秒,timer阶段还没有成功的回调。所以timer再后面输出。 ps:修改文件名即可看到效果~
比较setTimeout和setImmediate
1 | // 比较 setTimeout 和 setImmediate 在不同情况下的优先级 |
commonjs中this指向问题
模拟module.exports,exports和this初始化阶段指向
1 | // var temp = {}; |
案例:
1 | // index.js |