《理解JS: 事件循环机制》
Posted 杨晓风-linda
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《理解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中异步机制以及相关面试题有了深入的理解。
如下为开头面试题答案:
第一道:
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最后输出。
巨人肩膀:
以上是关于《理解JS: 事件循环机制》的主要内容,如果未能解决你的问题,请参考以下文章