本文内容:
Node.js
一个最简单的入门学习笔记,可能大部分参考了Node.JS
中文网
简单地Web服务器
const http = require('http') |
这里引入了http
模块
设定了本地监听的地址和监听端口号为127.0.0.1
和3000
同时使用http.createServer
来创建服务,这里的http.createServer
是非阻塞异步函数,采用了回调的方式来进行异步处理请求和发送响应包
其中(req, res) => {...}
采用了箭头函数
来创建
其中设置了响应包的statusCode
以及Header
,气质最后的消息体设置了你好世界
同时server.listen
在监听本地之后,就会触发回调函数来打印出我们监听的地址和端口号
程序退出
const express = require('express') |
对于这个Web服务器
,正常情况下是永远不会正常退出的(即除了你在外面Ctrl+C
)
如果说想在程序的其他地方关闭server
,如果采用process.exit()
退出,则会退出整个程序,可能正在等待或者运行的程序都会退出
但这种采用信号量SIGTERM
来进行进程处理
const express = require('express') |
SIGKILL
是告诉进程要立即终止的信号,理想情况下,其行为类似于process.exit()
SIGTERM
是告诉进程要正常终止的信号。它是从进程管理者(如upstart
或supervisord
)等发出的信号
如果说程序内部的其他函数想要结束进程是,只需要调用
process.kill(process.pid, 'SIGTERM') |
外部程序需要得知运行的Node
的进程pid
即可发送信号关闭
Node.js环境变量
可以通过
process.env |
来获取和修改
Node.js命令行参数
process.argv.forEach((val, index) => { |
可以看到通过process.argv
得到了命令行参数,通过forEach
函数以及回调函数来列举出来
不过这里同样打印出了我们的node
和js文件路径
这里可以通过后面加上slice(2)
指出从第三个参数开始
process.argv.slice(2).forEach((val, index) => { |
大概先这样,对于复杂的name=a2u13
以及--name=a2u13
需要第三方库解析即可,这里先不赘述
exports暴露接口
const car = { |
const obj = require('./sky') |
打印结果如下:
{ brand: 'Ford', model: 'Fiesta' } |
如果使用module.exports
来暴露
const car = { |
const car = require('./sky') |
{ brand: 'Ford', model: 'Fiesta' } |
不同的是,module.exports
只暴露了一个对象作为接口
npm运行任务
npm run <task-name> |
向package.json
文件中写入即可
{ |
npm run watch |
package.json属性
http://nodejs.cn/learn/the-package-json-guide
这里不复制粘贴了,有需求可以自己查
Node.js事件循环
调用堆栈
Javascript
几乎所有的I/O
操作比如文件读取、网络请求都是非阻塞
的,如果发现被阻塞了,不好意思出异常了
事件调用堆栈是后进先出的(LIFO
),即后边来的事件优先处理
事件循环不断地检查调用堆栈,以查看是否需要运行任何函数。
当执行时,它会将找到的所有函数调用添加到调用堆栈中,并按顺序执行每个函数。
const bar = () => console.log('bar') |
简单地例子,按照LIFO
的处理顺序,函数执行的打印应该如下:
foo |
感觉是不是顺序执行?
图片来自于node.js中文网
http://nodejs.cn/learn/the-nodejs-event-loop
从第一张堆栈图来看,node
在每次处理一个函数时,会依次加载内部函数到堆顶,然后如果内部函数同样具有结构体,则同理压入栈低,如果执行的是一个包装好的函数(console.log
),则执行完直接出栈
消息队列
const baz = () => console.log('baz') |
这里bar
本来是个单独函数,但我感觉这么写可以体现出我们回调的特性,就这么写了
执行上面的程序,打印结果如下:
foo |
为什么这里设置了sertTimeout
为0
,还是会将回调推迟到最后呢?
通过上图执行流程可知,node
在遇到setTimeout
之后,会将setTimeout
出栈,但是他所调用的回调函数,却被放入了消息队列
在消息队列中,用户触发的事件(如单击或键盘事件、或获取响应)也会在此排队,然后代码才有机会对其作出反应。 类似 onLoad
这样的 DOM 事件也如此。
事件循环会赋予调用堆栈优先级,它首先处理在调用堆栈中找到的所有东西,一旦其中没有任何东西,便开始处理消息队列中的东西。
消息队列的等待执行状态实际上也不是由setTimeout
所维持,是由浏览器提供支持(这里我有个疑问,对于node
而言,是谁在维持这个状态呢?)
总而言之
处理优先级:调用堆栈>消息队列
作业队列
const baz = () => console.log('baz') |
这个玩意我可以理解为我要插队了
执行结果为
foo |
这种方式会尽快地执行异步函数的结果,而不是放在调用堆栈的末尾。
在当前函数结束之前 resolve
的 Promise
会在当前函数之后被立即执行。
调用优先级:调用堆栈>作业队列>消息队列
process.nextTick
这个玩意我可以理解为:
他在处理完当前事件循环流程后,就要执行
告诉V8
要尽快处理这个任务,而不是把他插入队列当中
优先级:调用堆栈>process.nextTick>作业队列>消息队列
setImmediate
作为 setImmediate()
参数传入的任何函数都是在事件循环的下一个迭代中执行的回调。
setImmediate()
与 setTimeout(() => {}, 0)
、process.nextTick()
有何不同?
process.nextTick()
的函数会在事件循环的当前迭代中(当前操作结束之后)被执行。 这意味着它会始终在setTimeout
和setImmediate
之前执行。延迟 0 毫秒的
setTimeout()
回调与setImmediate()
非常相似。 执行顺序取决于各种因素,但是它们都会在事件循环的下一个迭代中运行。
可以看到process.nextTick
很牛逼,抢先执行,而setImmediate
和setTimeout
就很low
,不过给setTimeout
加点延迟,效果就不一样了
JavaScript Promise
Promise
是用来处理异步代码的一个感觉很NB的东西
当 promise 被调用后,它会以 处理中状态 开始。 这意味着调用的函数会继续执行,而 promise
仍处于处理中直到解决为止,从而为调用的函数提供所请求的任何数据。
被创建的 promise 最终会以被解决状态或被拒绝状态结束,并在完成时调用相应的回调函数(传给 then
和 catch
)。
可能直接写概念不好理解,我这里按照例子写了一些个人的看法理解
const fs = require('fs') |
重点在于reject、resolve、then、catch
可以看到是通过resolve
传入在外部通过then
来处理
如果我们传入的文件不存在呢?
则通过reject
传递进入了catch
处理
剩下的感觉很牛逼,到时候专门开个专题来写一下Promise
的有关知识
async/await
异步这一块有时间再写一下他的实现原理
Node.js 事件触发器
感觉就是定下个事件任务,然后直接触发
const EventEmitter = require('events') |
Node.js读写文件
读文件:
异步:
const fs = require('fs') |
同步:
const fs = require('fs') |
写文件:
异步:
const fs = require('fs') |
r+
打开文件用于读写。w+
打开文件用于读写,将流定位到文件的开头。如果文件不存在则创建文件。a
打开文件用于写入,将流定位到文件的末尾。如果文件不存在则创建文件。a+
打开文件用于读写,将流定位到文件的末尾。如果文件不存在则创建文件。
同步:
const fs = require('fs') |
模块方法
异常处理
Node.js
抛出异常不是抛出一个字符串常量,而是抛出一个错误对象
throw new Error('Ran out of coffee') |
处理异常则和Python
我感觉差不多
try { |
对于没有捕获的异常,则需要采用process
来进行监听捕捉
process.on('uncaughtException', err => { |
对于异步的错误,比如Promise
doSomething1() |
下面的例子通过打破链条而实现了每个then
的捕捉(这样方便得到错误的位置?
对于async
和await
async function someFunction() { |
其他
至于Buffer
以及Stream
等模块,我感觉这方面内容可能有些复杂
就不打算这么略写了,等遇到实际应用场景则来看看