《理解JS: 事件循环机制》
从面试题了解事件循环机制:
第一道:
//请写出输出内容
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
第二道:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改:
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)async1();
new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});
console.log('script end');
第三道:
async function async1() {
console.log('async1 start');
await async2();
//更改如下:
setTimeout(function() {
console.log('setTimeout1')
},0)
}
async function async2() {
//更改如下:
setTimeout(function() {
console.log('setTimeout2')
},0)
}console.log('script start');
setTimeout(function() {
console.log('setTimeout3');
}, 0)async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
第四道:
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}console.log('script start')setTimeout(() => {
console.log('setTimeout')
}, 0)Promise.resolve().then(() => {
console.log('promise1')
})a1()let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
第五道:
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
如果不太理解其中所蕴含的道理,点击下:体现事件循环机制过程在线网站
如果能正确答出上面题的输出内容,并能给予背后原理的解释,那证明读者对于JS中事件循环机制了解得挺深入的哦~
之前小编对于JS中事件循环机制了解也是模棱两可,近来对此部分进行了进一步的学习,先以一张图来说明学习成果:
文章图片
想要了解JS中的事件循环机制,咱们需要了解如下内容:
- event loop:主线程从“任务队列”中循环读取任务。而循环读取任务的先后顺序,则取决于任务队列(Job queue)中对于不同任务读取规则的限定
- Job queue:分为两种类型:macro-task和micro-task,即为宏任务和微任务,微任务的执行优先级高于宏任务
文章图片
- 浏览器是多进程的,JS是单线程的。浏览器除了JS引擎之外,还有Web APIs线程、GUI线程等, JS引擎在执行过程中,如果遇到相关的事件(DOM操作、AJAX请求、滚轮事件、鼠标点击事件、setTimeout等),并不会因此阻塞,它会将这些事件移交给Web APIs线程处理,而自己接着往下进行。
- setTimeout:设置的时间是最小延迟时间,并不是确切的等待时间,实际上最小延迟>=4ms,小于4ms的被当做4ms
- Web APIs:按照一定的规则将这些事件放入一个任务队列(Callback queue,也叫task queue)
- 任务队列:在HTML标准定义中,任务队列的数据结构不是队列,而是Set集合
文章图片
- JS单线程:不想并行操作DOM,DOM树不是线程安全的,如果是多线程,会引起冲突。只有一个执行栈(call stack),在同一时刻,JS引擎只能做一件事情,并且只要运行就直到完成(run to complete)。如果是这样的话,那么我网页后端请求数据的时候,为什么我还能点击按钮、滚动页面呢?原因是因为浏览器是多进程的,如下图所示的事件循环模型:
文章图片
说明:
1、JS线程负责处理JS代码,当遇到一些异步操作的时候,则将这些异步事件移交给Web APIs处理,自己则继续往下执行
2、Web APIs线程将接收到的事件按照一定的规则添加到任务队列中,宏事件添加到宏任务队列中,微事件添加到微任务队列中
3、JS线程处理完当前的所有任务以后(执行栈为空),它会先去微任务队列中获取事件,并将微任务队列中按照先进先执行的顺序一件件执行完毕,直到微任务队列为空后,再去宏任务队列中取出一个事件执行
(每次取完一个宏任务队列中的事件执行完毕后,都先检查微任务队列)
4、然后不断循环第三步。
- new Promise:在使用new关键字来创建Promise对象时,传递给Promise的函数称为executor,当promise被创建的时候,executor函数会自动执行,而then里面的内容才是异步执行的部分。
- async/await:async是Generator函数的语法糖,await关键字会将后面的内容包装成promise交给Web APIs处理,执行栈会跳出async函数继续执行,直到promise执行完并返回结果。
文章图片
在这里,小编第一次也并没有理解async/await关键字的作用,在第二遍学习的时候理解了,请看如下示例:
源代码:
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}async function async2() {
console.log('async2')
}
其实,根据await关键字的作用,会将后面的内容包装成promise,然后应该是如下模样:
async function async1(){
console.log('async1 start')
new promise((reslove)=>{
console.log('async2')
}).then(()=>{
console.log('async1 end')
})
}
基本上在理解了如上相关内容后,对于JS中异步机制以及相关面试题有了深入的理解。
【《理解JS: 事件循环机制》】如下为开头面试题答案:
第一道:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
第二道:
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout
第三道:
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1
第四道:
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
第五道:
//输出的是2 6 5 1 3 4//区别在于promise的构造中,没有同步的resolve
,因此promise.then在当前的执行队列中是不存在的,
只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,因此3,4最后输出。
巨人肩膀:
谈谈Event Loop中的Job queue
事件循环(event loop)以及异步执行顺序
推荐阅读
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 《跨界歌手》:亲情永远比爱情更有泪点
- 诗歌:|诗歌: 《让我们举起世界杯,干了!》
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 人间词话的智慧
- 《一代诗人》37期,生活,江南j,拨动心潭的一泓秋水
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术
- 书评——《小行星》