一、javascript是一门单线程语言
二、js运行机制
1、浏览器中
同步任务和异步任务
- 同步任务:比如页面骨架和页面元素的渲染
- 异步任务:比如加载图片音乐之类占用资源大耗时久的任务
* 看图==>
(1)同步的进入主线程,异步的进入Event Table并注册函数
(2)当指定的事情完成时,Event Table会将这个函数移入任务队列
(3)主线程内的任务执行完毕为空,会去任务队列读取对应的函数,进入主线程执行
(4)上述过程会不断重复,也就是常说的Event Loop(事件循环)。
macro-task(宏任务) 和 micro-task(微任务)
macro-task(宏任务):整体代码script、setTimeout、setInterval
micro-task(微任务):Promise、await、process.nextTick(nodejs)
* 碰到宏任务和微任务怎么执行呢
(1)执行整体代码script(宏任务)
(2)执行所有的微任务
(3)再执行一个宏任务
(4)然后再执行所有的微任务,一直循环
鄙视题了解一下
console.log('1');
setTimeout(function() { // 宏1
console.log('2');
process.nextTick(function() { // 微1
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() { // 微2
console.log('5')
})
})
process.nextTick(function() { // 微3
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() { // 微4
console.log('8')
})
setTimeout(function() { // 宏2
console.log('9');
process.nextTick(function() { // 微5
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() { // 微6
console.log('12')
})
})
解析==============>
(1)执行整体代码(宏), 打印出 1 7
,在这期间,挂起的异步任务有
宏 | 微 |
---|---|
宏1 宏2 | 微3 微4 |
(2)开始执行所有微任务(微3、微4),打印出6 8
,此时任务队列剩余
宏 | 微 |
---|---|
宏1 宏2 |
(3)执行一个宏任务(宏1),打印出2 4
,并且在这期间,又挂载了新的异步任务(微1,微2),因此任务队列剩余
宏 | 微 |
---|---|
宏2 | 微1 微2 |
(4)执行所有微任务(微1,微2),打印出3 5
,任务队列剩余
宏 | 微 |
---|---|
宏2 |
(5)执行一个宏任务(宏2),打印出9 11
, 同时又挂载了新的异步任务(微5, 微6),任务队列剩余
宏 | 微 |
---|---|
微5 微6 |
(6)执行所有微任务(微5, 微6),打印出10 12
因此,最后执行结果为 1 7 6 8 2 4 3 5 9 11 10 12
再看一道=============================>
async function async1() {
console.log('async1 start'); //=>2
await async2();
console.log('async1 end'); //=>6 // 微1
}
async function async2() {
console.log('async2'); //=>3
}
console.log('script start'); //=>1
setTimeout(function () { // 宏1
console.log('setTimeout'); //=>9
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1'); //=>4
resolve();
}).then(function () { // 微2
console.log('promise2'); //=>7
}).then(function () { // 微3
console.log('promise3'); //=>8
});
console.log('script end'); //=>5
上面的数字代表执行顺序,解析开始==========》
(1)执行整体代码(宏),打印出script start
async1 start
async2
promise1
script end
此时任务队列剩余
宏 | 微 |
---|---|
宏1 | 微1 微2 微3 |
(2)依次执行所有微任务(微1 微2 微3),打印出async1 end
promise2
promise3
此时任务队列剩余
宏 | 微 |
---|---|
宏1 |
(3)执行一个宏任务(宏1),打印出setTimeout
因此执行结果==============>script start
async1 start
async2
promise1
script end
async1 end
promise2
promise3
setTimeout
再鄙视
setTimeout(function () {
console.log('a');
new Promise(function (resolve, reject) {
resolve();
console.log('b');
}).then(function () {
console.log('c')
})
}, 1000)
setTimeout(function () {
console.log('d');
}, 0)
new Promise(function (resolve, reject) {
reject();
console.log('e')
}).then(function () {
console.log('f')
}).catch(() => {
console.log('g')
})
console.log('h')
2、nodejs运行机制
nodejs的event loop分为6个阶段,它们会按照顺序反复运行,分别如下:
timers
:执行setTimeout() 和 setInterval()中到期的callback。I/O callbacks
:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
除了以下操作的回调函数,其他的回调函数都在这个阶段执行。
- setTimeout()和setInterval()的回调函数
- setImmediate()的回调函数
- 用于关闭请求的回调函数,比如socket.on('close', ...)
idle, prepare
:队列的移动,仅内部使用poll
:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段check
:执行setImmediate的callbackclose callbacks
:执行close事件的callback,例如socket.on("close",func)
node中的几个特点需要说明一下
- 上面六个阶段的任务都是宏任务
- promise.then和process.nextTick()是微任务;且process.nextTick()优先级要高于promise
- setTimeout(fn,x); x>0时候会跳过,x=0也可能跳过,因为node至少也得1ms左右
node中的执行顺序如下
(1)执行整体代码(同步宏任务)
(2)执行微任务(process.nextTick()、promise.then)
(3)按event loop六个阶段执行(中间夹杂着微任务)
练习题1
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() { // 微1
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() { // 微2
console.log('promise2')
})
}, 0)
解析:
1、执行整体代码,没啥输出
2、执行微任务 process.nextTick()、promise.then,也没有
3、开始执行event loop得timer阶段,发现有两个setTimeout,执行后输入timer1、time2
, 同时又挂载了两个微任务(微1,微2)
4、执行微任务(微1 微2)输出promise1,promise2
结果:timer1、time2、promise1,promise2
练习题2
const fs = require('fs');
fs.readFile('test.js', () => {
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
});
解析:
1、执行整体代码,没啥输出
2、执行微任务 process.nextTick()、promise.then,也没有
3、开始执行event loop, 但timer阶段也没发现setTimout,和setInterval,那就跳过了
4、然后到了I/O callback, 发现有readFile得回调,执行里面代码,但没输出值,只是将 setTimeout放到了timer间段, 把setImmediate放到了check间段
5、然后执行微任务,还是没有process.nextTick()、promise.then
6、依次进入到check阶段,发现有setImmediate,执行输入出2
7、依次进入下一次循环,进入timer, 发现了setTimout, 执行输出1
结果:2 1
练习题3
setTimeout(function () {
console.log(1);
},0);
console.log(2);
process.nextTick(() => {
console.log(3);
});
new Promise(function (resolve, rejected) {
console.log(4);
resolve()
}).then(res=>{
console.log(5);
})
setImmediate(function () {
console.log(6)
})
console.log('end');
就不解析了,一个道理,结果:2 4 end 3 5 1 6
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。