nodejs api学习

1.Assert

用于测试数据,比较数据是否相等

1.1 assert(value[, message])

第一个参数是值,第二个参数是异常时抛出的异常信息,第二个参数可不写,不写就是系统默认的错误信息

例子1:

var assert = require("assert")


assert(false,"第一个值为false时以我为错误信息抛出");

终端抛出:

node:assert:400
    throw err;
    ^

AssertionError [ERR_ASSERTION]: 第一个值为false时以我为错误信息抛出
    at Object.<anonymous> (F:\nodeDemo\16.assert.js:7:1)
    at Module._compile (node:internal/modules/cjs/loader:1126:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
    at Module.load (node:internal/modules/cjs/loader:1004:32)
    at Function.Module._load (node:internal/modules/cjs/loader:839:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47 {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '=='
}

把值改成true时,执行后面的代码

1.2 assert.ok(value[, message])

用法与assert(value[, message])一样

1.3 assert.equal(actual, expected[, message])

用于测试预期值与实际值是否相等,如果相等则断言通过,否则抛出message,相等于==

例子:

var assert = require("assert")

var a = 1+1

assert.deepEqual(a,1,'值不相等')

1.4 assert.deepEqual(actual, expected[, message])

用于测试预期值与实际值是否相等,相当于==

equal的区别,deepEqual比较的是对象的属性值

const obj={a:1};
const obj2={a:1};
assert.equal(obj,obj2,'equal 不等');//false
assert.deepEqual(obj,obj2,'deepEqual 不等')//true

1.5 assert.deepStrictEqual(actual, expected[, message])

用于测试预期值与实际值是否相等,相当于===

1.6 assert.strictEqual(actual, expected[, message])

用于测试预期值与实际值是否相等,相当于===

1.7  assert.notEqual(actual, expected[, message])

2.Buffer

创建Buffer对象

2.1 Buffer.from()

        支持String、Array、ArrayBuffer、Object、buffer五种类型

--Buffer.from(String [,encoding]);

--Buffer.from(Array);

--Buffer.from(object[, offsetOrEncoding[, length]]);

2.2 Buffer.alloc(size[, fill[, encoding]])

2.3 Buffer.allocUnsafe()

2.4 Buffer.allocUnsafeSlow()

Buffer对象的属性和方法

2.5 Buffer.concat(list[, totalLength])

这是一个拼接Buffer的静态方法,参数list是所有拼接的Buffer对象的列表,totalLength是指定拼接成新的Buffer对象的长度,如果长度不够则会忽略后面数据

2.6 Buffer.isBuffer(obj)

这是一个Buffer静态方法,用来判断对选是否是一个Buffer对象。

2.7 Buffer.keys()

这是一个Buffer对象方法(公有方法),用来获取Buffer对象的key迭代器

2.8 buf.subarray(start,end)

subarray获取Buffer对象中的指定片段,与字符串拼接时会默认调用toString方法

string_decoder可以解决拼接时乱码

3.child_process

child_process模块用于新建子进程,子进程的运行结果储存在系统缓存之中(最大200KB),等到子进程运行结束以后,主进程再用回调函数读取子进程的运行结果。

3.1 child_process.exec(command[, options][, callback])

执行的是非node程序,是一个shell命令,执行结果以回调的形式返回。

3.2 child_process.execFile(file[, args][, options][, callback])

执行的是非node程序,是一个可执行文件或应用,执行结果以回调的形式返回

3.3 child_process.spawn(command[, args][, options])

执行的是非node程序,是一个shell命令,不需要获取执行结果,执行结果以流的形式返回。

3.4 child_process.fork(modulePath[, args][, options])

执行的是node程序,是一个.js文件,不需要获取执行结果,执行结果以流的形式返回,fork出来的进程一定是node进程。

子进程与父进程相互通信

在子进程中:

通过process.on('message')和process.send()的机制来接收和发送消息。

在父进程中:

通过child.on('message')和process.send()的机制来接收和发送消息。

4.cluster

4.1 isMaster,isWorker

区分时主进程还是工作进程

4.2 fork()

主进程中创建一个工作进程

4.3 kill()

终止工作进程

5. copyto 加密解密

const crypto = require('crypto');

 
function encrypto(key,data) { // 加密函数
    //key : 公钥   data:需要加密的数据,buffer格式
    return crypto.publicEncrypt(key,Buffer.from(data));
 }
 
function decrypto(key,encrypted) { //解密函数
    //key : 私钥  encrypted: 解密的数据 , buffer格式
    return crypto.privateDecrypt(key,encrypted)
 }
 
 const str = '我真帅'//需要加密的
 
 const cryptoed = encrypto(public_key,str) //公钥加密
 const decryptoed = decrypto(private_key,cryptoed) //私钥解密

6.event

一个对象通过这个模块,向另一个对象传递消息

eventEmitter.on() 用于注册监听器

eventEmitter.emit() 用于触发事件

eventEmitter.once() 只触发一次

自定义的类或者对象都是没有实现或继承 Events 模块的,想要使用 Events 模块就需要先导入该模块。

类的继承event

const event = require('events')

class People extends event {
    constructor(name) {
        super()
        this.name = name
        this.on('eat', this.eat)
        
    }

    eat() {
        console.log(`${this.name} eat `);
        this.emit('age')
    }

}

module.exports = People

使用:

const PeopleEvent = require('./peopleevent ')

const tom = new PeopleEvent('tom')

tom.on('age', () => {
    console.log(`${tom.name}'age is 18 `);
})

tom.emit('eat')

输出:

tom eat 
tom'age is 18 

还可以使用utilinherits方法继承(这种时过时的)

const eventEmitter = require('events').EventEmitter
const util = require('util')

const people = function People(name) {

    this.name = name
    this.on('eat', eat)

    function eat() {
        console.log(`${this.name} eat `);
        this.emit('age')
    }
}

util.inherits(people, eventEmitter)
module.exports = people;

使用:

const People = require('./60.event')

const tom = new People('Tom')

tom.on('age', () => {
    console.log(`${tom.name}'age is 18 `);
})

tom.emit('eat')

输出时同样的 

tom eat 
tom'age is 18 

需要先注册在发布

7.fs

fs.readFileSync(fileName, "utf8");

        用于同步读取文件,返回一个字符串

        第一个参数是文件路径,第二个参数是文本文件编码默认为utf8

fs.writeFileSync(fileName, str, 'utf8');

        同步写入文件

        它的第一个参数是文件路径,第二个参数是写入文件的字符串,第三个参数是文件编码,默认为utf8

fs.exists(path, callback)

        判断给定路径是否存在,然后不管结果如何,都会调用回调函数

        fs.exists('/path/to/file', function (exists) {}

        exists是一个布尔值

        不要在open方法之前调用exists方法,open方法本身就能检查文件是否存在

fs.readFile(path[,options],callback) 

        用于异步读取文件

let fs = require('fs');
fs.readFile('example_log.txt', function (err, logData) {
  if (err) throw err;
  let text = logData.toString();
});

fs.writeFile(file,data[,options],callback)

        用于异步写入文件

fs.stat检测文件还是目录/文件大小等

fs.mkdir创建目录

        mkdir接受三个参数,第一个是目录名,第二个是权限值,第三个是回调函数

        0777 表示可读可写可执行

        0666 表示可读可写

let fs = require('fs');
fs.mkdir('./helloDir',0777, function (err) {
  if (err) throw err;
});

fs.appendfile 追加文件

fs.readdir读取目录

fs.rename

fs.rmdir删除目录  注意:目录下必须为空,才删除

fs.unlink 删除文件

8.module

require() 引入模块

module.exports 导出模块或值

Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令

let exports = module.exports;
不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系

内置的require命令用于加载模块文件

require命令用于加载文件,后缀名默认为.js

let foo = require('foo'); // 等同于 let foo = require('foo.js');

根据参数的不同格式,require命令去不同路径寻找模块文件:    

  1. 如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,require('/home/marco/foo.js')将加载/home/marco/foo.js
  2. 如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require('./circle')将加载当前脚本同一目录的circle.js
  3. 如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
  4. 如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径
  5. 如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。
  6. 如果想得到require命令加载的确切文件名,使用require.resolve()方法。

9.net

net.Server 创建服务器

1) 创建Server:var server = net.Server();
2) 监听已有的连接:server.listen(端口号,ip);
3) 监听事件使用on:
事件有: listening:当服务绑定后触发
connection:当一个新的connection(连接)建立的时候触发,有参数socket
close: 当server关闭的时候触发,注意要等到所有的连接都结束之后才触发
error: 当错误出现的时候触发

4) 方法:
listen:监听已有的连接
close:关闭连接
address:返回绑定的地址,只有在listening事件被触发的时候菜可以调用该事件,不然返回的是一个空对象

net.socket 可以被用户创建并直接与server通信,通过[net.createConnection()][]返回的

1) 创建一个连接:net.connect(端口号,ip);
2) 监听事件使用on:
事件有: connect:当一个socket连接成功建立的时候触发该事件
data:当接收到数据的时候触发该事件,数据编码由 socket.setEncoding() 设置
end: 关闭连接
timeout:连接超时触发
error:
3) 方法:
connect(端口,IP):创建连接
write(内容,编码格式-默认UTF8):在socket上发送数据
setTimeout():设置超时时长

4) 属性:
localPort:用数字表示本地端口。例如80或21
localAddress:返回操作系统报告的 socket 的地址、地址族和端口。返回的对象有三个属性,例如: { port: 12346, family: ‘IPv4’, address: ‘127.0.0.1’ }
remotePort:用数字表示远程端口
remoteAddress:用字符串表示远程IP地址

10.path

path.basename(path[, ext])  返回文件名字,添加第二个参数去掉文件后缀

path.dirname() 返回父路径

path.extname() 方法返回 path 的扩展名

path.format() 方法会从一个对象返回一个路径字符串

path.isAbsolute() 方法会判定 path 是否为一个绝对路径

path.join() 方法使用平台特定的分隔符把全部给定的 path 片段连接到一起,并规范化生成的路径

path.parse() 方法返回一个对象,对象的属性表示 path 的元素

path.parse('/home/user/dir/file.txt');
// 返回:
// { root: '/',
//   dir: '/home/user/dir',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file' }

path.relative() 方法返回从 from 到 to 的相对路径 

path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径

给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径。 例如,给定的路径片段的序列为:/foo/barbaz,则调用 path.resolve('/foo', '/bar', 'baz') 会返回 /bar/baz

11.querystring

querystring.escape(str) 对给定的 str 进行 URL 编码。

querystring.parse(str[, sep[, eq[, options]]]) 该方法会把一个 URL 查询字符串 str 解析成一个键值对的集合

querystring.stringify(obj[, sep[, eq[, options]]]) 该方法通过遍历给定的 obj 对象的自身属性,生成 URL 查询字符串

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge='

12 readline

基本用法:

//引入readline模块
const readline = require('readline');
//创建readline接口实例
let r1 = readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
//使用question方法
r1.question('你想吃什么?',function (anwser){
    console.log(`我想吃${anwser}`);
    //添加close事件,不然不会结束
    r1.close();
});
//close事件监听
r1.on('close',function (){
    //结束程序
    process.exit(0);
});

readline.Interface 类的实例是使用 readline.createInterface() 方法构造的

当 'close' 事件被触发时,readline.Interface 实例会被结束

line事件:每当 input 流接收到接收行结束符(\n\r 或 \r\n)时触发 'line' 事件。 通常发生在用户按下 <Enter> 键或 <Return> 键。

注册line事件:

rl.on('line', (input) => {
  console.log(`接收到:${input}`);
});

rl.question() 方法通过写入到 output 来展示 query,并等待用户提供到 input 的输入,然后调用 callback 函数并传入提供的输入作为第一个参数。

13 stream

Node.js 中有四种基本的流类型:

pipe:在可读流(readable stream)上调用 stream.pipe() 方法,并在目标流向 (destinations) 中添加当前可写流 ( writable ) 时,将会在可写流上触发 'pipe' 事件

readableSrc.pipe(writableDest)

可读流

  • data 事件,当流传递给消费者一个数据块的时候会触发。
  • end 事件,当在流中没有可以消费的数据的时候会触发。
const fs = require('fs');
const file = fs.createReadStream('./msg.txt', {
  flags: 'r', // 文件的操作方式,同readFile中的配置一样,这里默认是可读的是 r
  encoding: 'utf-8', // 编码格式
  autoClose: true, // 是否关闭读取文件操作系统内部使用的文件描述符
  start: 0, // 开始读取的位置
  end: 5, // 结束读取的位置
  highWaterMark: 1 // 每次读取的个数
});

file.on('open', () => {
  console.log('开始读取文件');
});

file.on('data', (data) => {
  console.log('读取到的数据:');
  console.log(data);
});

file.on('end', () => {
  console.log('文件全部读取完毕');
});

file.on('close', () => {
  console.log('文件被关闭');
});

file.on('error', (err) => {
  console.log('读取文件失败');
});

暂停事件 file.pause();重新读取,需要使用 resume()方法

可写流

  • drain 事件,当可写流可以接受更多的数据时的一个标志。
  • finish 事件,当所有的数据都写入到底层系统中时会触发。
const fs = require('fs')

let rs = fs.createReadStream('./tools/赵雷 - 成都.mp3', { highWaterMark: 1024 })

let ws = fs.createWriteStream('./chengdu.mp3', { highWaterMark: 1024 })

function sleep ( n,rs ) { 
    let start = new Date().getTime() ;
    while ( true ) {
        if ( new Date( ).getTime( ) - start > n ) {
            console.log('恢复');
            rs.read()
            rs.resume()
            break;
        }
    }
}

let i = 0
console.log('初始',i)
rs.on('data', function(chunk) {
    console.log('读取1024', i)
    i++
    console.log('当前长度', i)
    if (10 === i) {
        rs.pause()
        sleep(2000, rs)  
    }
    console.log('读取进度', Math.trunc( (i * 1024)/13393628 * 100 ),'%')
    // 当ws.write() 返回false时,表示没有空间继续写入了,暂停读取
    if(ws.write(chunk) == false) {
        console.log('暂停rs的data事件');
        rs.pause() // 暂停rs的data事件
    }
})

ws.on('drain', function() {
    rs.resume() // 恢复rs的data事件        // 把当前读入的内容都写到文件中了,继续调用读写
})
// 当读取流触发end方法,表示读取完毕,这时关闭可写流的写入
rs.on('end', function() {
    ws.end()
})

管道流

const fs = require('fs');

// 读取msg.txt中的字符串 hello world
const msg = fs.createReadStream('./msg.txt', {
  highWaterMark: 5
});

// 写入到1.txt中
const f1 = fs.createWriteStream('./1.txt', {
  encoding: 'utf-8',
  highWaterMark: 1
});

const res = msg.pipe(f1);
console.log(res);

14.定时器

setImmediate(callback[, ...args])

预定立即执行的 callback,它是在 I/O 事件的回调之后被触发

setInterval(callback, delay[, ...args])

预定每隔 delay 毫秒重复执行的 callback

setTimeout(callback, delay[, ...args])

预定在 delay 毫秒之后执行的单次 callback

16.URL

new URL(input[, base])

const { URL } = require('url');
const myURL = new URL('/foo', 'https://example.org/');
  // https://example.org/foo

17.Process 

process对象是Node的一个全局对象,提供当前Node进程的信息。它可以在脚本的任意位置使用,不必通过require命令加载。该对象部署了EventEmitter接口。

process.argv:返回当前进程的命令行参数数组。
process.env:返回一个对象,成员为当前Shell的环境变量,比如process.env.HOME。
process.installPrefix:node的安装路径的前缀,比如/usr/local,则node的执行文件目录为/usr/local/bin/node。
process.pid:当前进程的进程号。
process.platform:当前系统平台,比如win32。
process.title:默认值为“node”,可以自定义该值。
process.version:Node的版本,比如v16.17.1。

process.stdout: 标准输出,​​​​​​​它的write方法等同于console.log,可用在标准输出向用户显示内容

process.stdout.write('ssssss');//console.log('ssssss);

 输出文件

fs.createReadStream('./files/input.txt')
  .pipe(process.stdout)

process.stdin: 标准输入

process.stdin.setEncoding('utf8');
let fs = require('fs');
process.stdin.on('readable', function() {
    let chunk = process.stdin.read();
  if (chunk !== null) {
    process.stdout.write('data: ' + chunk);
  }
});

将键盘输入的显示在控制台

process.stdin.pipe(fs.createWriteStream('./files/input3.txt'))

将键盘输入的显示在文件上

process.stderr: 指向标准错误

process.chdir():切换工作目录到指定目录。
process.cwd():返回运行当前脚本的工作目录的路径。
process.exit():退出当前进程。
process.getgid():返回当前进程的组ID(数值)不支持window系统
process.getuid():返回当前进程的用户ID(数值)。
process.nextTick():指定回调函数在当前执行栈的尾部、下一次Event Loop之前执行。
process.on():监听事件,并指定回调函数 比如uncaughtException。
process.setgid():指定当前进程的组,可以使用数字ID,也可以使用字符串ID。
process.setuid():指定当前进程的用户,可以使用数字ID,也可以使用字符串ID。


 process.exit() 来指定退出代码,直接退出,这会导致事件循环中的任务直接不被处理,以及可能导致数据的截断和丢失

正确安全的处理是,设置 process.exitCode,并允许进程自然退出

当前进程退出时,会触发exit事件,可以对该事件指定回调函数

process.on('exit', function () {
  fs.writeFileSync('/tmp/myfile', '需要保存到硬盘的信息');
});

注意,此时回调函数只能执行同步操作,不能包含异步操作,因为执行完回调函数,进程就会退出,无法监听到回调函数的操作结果​​​​​​​

当 Node.js 清空其事件循环并且没有其他工作要安排时,会触发 beforeExit 事件。例如在退出前需要一些异步操作,那么可以写在 beforeExit 事件中

let hasSend = false;
process.on("beforeExit", () => {
 if (hasSend) return; // 避免死循环

 setTimeout(() => {
  console.log("mock send data to serve");
  hasSend = true;
 }, 500);
});

console.log(".......");
// 输出:
// .......
// mock send data to serve

 异常处理 try-catch 捕获异常,如果异常未捕获,可以通过监听 process 的 uncaughtException 事件,来处理未捕获的异常

process.on("uncaughtException", (err, origin) => {
 console.log(err.message);
});

const a = 1 / b;
console.log("abc"); // 不会执行

process.nextTick 把回调函数作为微任务,放入事件循环的任务队列中

结合使用:

process.on('uncaughtException', (err, origin)=>{
    console.log(err.message);
})

process.nextTick(function () {
    console.log("next tick1"); 
    sess()
    console.log("next tick11"); //不会执行
 }); 

 process.nextTick(function () {
    console.log("next tick2"); 
 }); 

 console.log("immediate");

输出:

immediate
next tick1
sess is not defined
next tick2

18.Node.js 单线程异步非阻塞模式

同步阻塞方式:

比如,你打电话问老婆今晚吃什么,老婆在电话那边一直想啊想,你在电话这边不干别的,就一直等啊等,电话始终未挂,直到她说吃火锅,电话才挂掉。

同步非阻塞方式:

比如,你打电话问老婆今晚吃什么,老婆在电话那边一直想啊想,你在电话这边该干什么干什么,电话始终未挂,直到她说吃火锅,电话才挂掉。

异步阻塞方式:

比如,你打电话问老婆今晚吃什么,老婆说我想想,过一会跟你打话。你在电话这边什么也没干,就一直等着这个电话。

异步非阻塞方式:

比如,你打电话问老婆今晚吃什么,老婆说我想想,过一会跟你打话。你在电话这边想干什么干什么,如果有电话来了,再处理电话。

同步与异步是被调用方决定的,决定是马上给你答案,还是过会通知你,给你答案。

阻塞与非阻塞是调用方决定的,在等待结果的过程中, 是否还可以干其他事。

同步阻塞例子:

console.log('开始读文件');
let fileContext = fs.readFileSync('./files/input.txt')
console.log('input.txt 文件内容',fileContext.toString());
console.log('程序其他操作');
let file2Context = fs.readFileSync('./files/output.txt')
console.log('output.txt 文件内容',file2Context.toString());
console.log('程序结束');

异步非阻塞例子:

function ReadInput(){
   fs.readFile('./files/input.txt',(err,data)=>{
        console.log('input文件内容',data.toString());
    })
}
function ReadOutput(){
    return fs.readFile('./files/output.txt',(err,data)=>{
        console.log('output文件内容',data.toString());
    })
}
console.log('开始读input文件');
ReadInput()
console.log('开始读output文件');
ReadOutput()
console.log('程序结束');

19.HTTP / HTTPS / HTTP2

参考:https://www.jianshu.com/p/d9029f7227ea

https基于http的加密请求方式

http2是一个请求可以有多个响应

20.OS

序号方法 & 描述
1os.tmpdir()
返回操作系统的默认临时文件夹。
2os.endianness()
返回 CPU 的字节序,可能的是 "BE" 或 "LE"。
3os.hostname()
返回操作系统的主机名。
4os.type()
返回操作系统名
5os.platform()
返回编译时的操作系统名
6os.arch()
返回操作系统 CPU 架构,可能的值有 "x64"、"arm" 和 "ia32"。
7os.release()
返回操作系统的发行版本。
8os.uptime()
返回操作系统运行的时间,以秒为单位。
9os.loadavg()
返回一个包含 1、5、15 分钟平均负载的数组。
10os.totalmem()
返回系统内存总量,单位为字节。
11os.freemem()
返回操作系统空闲内存量,单位是字节。
12os.cpus()
返回一个对象数组,包含所安装的每个 CPU/内核的信息:型号、速度(单位 MHz)、时间(一个包含 user、nice、sys、idle 和 irq 所使用 CPU/内核毫秒数的对象)。
13os.networkInterfaces()
获得网络接口列表。
var os = require("os");

// CPU 的字节序
console.log('endianness : ' + os.endianness());

// 操作系统名
console.log('type : ' + os.type());

// 操作系统名
console.log('platform : ' + os.platform());

// 系统内存总量
console.log('total memory : ' + os.totalmem() + " bytes.");

// 操作系统空闲内存量
console.log('free memory : ' + os.freemem() + " bytes.");

21. 事件循环

js作为浏览器脚本,决定了它只能是单线程的,一些耗时任务会导致浏览器卡顿.因此js将任务分成了同步任务和异步任务.

同步和异步任务分别进入不同的执行”场所”,同步的进入线程,异步的进入任务列表,当线程里面的任务执行完成,从异步任务列表拿出任务在线程里面执行,当前的任务执行完成在从任务列表拿出来在线程里面执行,这个过程就是事件循环

异步任务又分为宏任务和微任务,不同的任务进入不同的任务栈.先执行所有的微任务,在进入宏任务执行微任务...

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

Promise 新建一个实例时,会立即执行

setTimeout(function() {
    console.log('setTimeout');
})
new Promise(function(resolve) {
    console.log('promise');
    resolve()
}).then(function() {
    console.log('then');
})
console.log('console');

//结果
promise
console
then
setTimeout

1.这段代码作为宏任务,进入主线程
2.先遇到setTimeout,加入宏任务队列
3.接下来遇到了Promise,new Promise立即执行,then函数加入到微任务队列
4.遇到console.log()同步任务,立即执行
5.好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行
5.ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行
结束
 

console.log('1');
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

// 1  7  6  8 2 4 3 5 9 11 10 12

1.这段代码作为宏任务,进入主线程

2.遇到console.log()同步任务,立即执行 输出1

3.遇到setTimeout,添加到到宏任务Event Queue

4.遇到process.nextTick,添加到到微任务Event Queue

5.遇到new Promise,立即执行,输出7

6.then函数加入到微任务队列

7.遇到setTimeout,添加到到宏任务Event Queue

8.好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?上面的4和6是微任务,

按先后顺序输出6,8

9.第一轮事件循环结束,开始第二轮,查找序号3宏任务执行,遇到console.log()同步任务,输出2

10.遇到process.nextTick,加入到微任务队列

11.遇到new Promise,立即执行,输出4

12.then函数加入到微任务队列

13.ok,当前的宏任务结束,查看微任务,查看微任务序号10,11,按顺序输出3,5

14.微任务执行完成,开始下一个宏任务setTimeout,序号7,,遇到console.log()同步任务,输出9

15.后面的与第一个setTimeout一样,输出11,10,12

微任务队列中,process.nextTick执行优先级高于其他微任务

宏任务根据类型不同分为以下几种:

Timers 类型的宏任务队列

  • setTimeout()

  • setInterval

Check 类型的宏任务队列

  • setImmediate()

Poll 类型的宏任务队列

  • 除了上面几种的其他所有回调

宏任务里面执行顺序:Timers --> Poll -->Check---

Timers 阶段执行将到期任务从任务栈取出执行

poll 阶段执行到期回调

const fs=require('fs')
fs.readFile('test.txt',()=>{
        console.log(' 0')
        setTimeout(()=>{
                console.log(' 1');
        })
        setImmediate(()=>{
                console.log(' 2')
        })
        setTimeout(()=>{
            console.log(' 3');
    })
})
setTimeout(()=>{
    console.log(' 4');
})

setTimeout(()=>{
    console.log(' 5');
},100)


setImmediate(()=>{
    console.log(' 6')
})

console.log(' 7');

1.进入主线程,readFile加入宏任务

2.setTimeout加入异步宏任务

3.setTimeout加入异步宏任务

4.setImmediate加入异步宏任务

5.同步任务,直接输出7

6.time阶段检测到到期的任务,输出4

7.poll阶段 读取文件还没有完成

8.check阶段 setImmediate输出6

9.进入下轮循环,setTimeout还没有到期

10.进入poll阶段,读取文件完成,输出0

11.check阶段 setImmediate输出2

12.将读文件回调里面的setTimeout加入宏任务队列

13.进入下轮循环,执行到期的setTimeout,输出1,3

14..进入poll阶段,没有任务,check阶段没有任务,下轮循环

15..time阶段检测到到期的任务,输出5

结果: 7 4 6 0 2 1 3 5

如果将打印1的事件间隔100执行,即

const fs=require('fs')
fs.readFile('test.txt',()=>{
        console.log(' 0')
        setTimeout(()=>{
                console.log(' 1');
        },100)
        setImmediate(()=>{
                console.log(' 2')
        })
        setTimeout(()=>{
            console.log(' 3');
    })
})
setTimeout(()=>{
    console.log(' 4');
})

setTimeout(()=>{
    console.log(' 5');
},100)


setImmediate(()=>{
    console.log(' 6')
})

console.log(' 7');

输出结果: 7 4 6 0 2 3 5 1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值