赞
踩
JS代码在浏览器中的执行过程在 JS高级——浏览器运行前端项目的原理及流程里面已经介绍了,但是文章中执行的代码都只是一般情况的代码执行,都是从上往下依次执行;实际上在开发中我们会经常使用网络请求(axios)、promiese、setTimeOut、setInterval等异步操作时,那么在执行代码时浏览器会按照什么样的执行顺序来执行呢?接下来让我们来了解浏览器的事件循环机制。
操作系统中的进程和线程,在这里就不过多解释了,不了解的小伙伴可以查询一下资料。
1.1 我们知道JavaScript是单线程的,它的容器进程是浏览器或Node。那么浏览器是单个进程吗?进程里面只有一个线程吗?
答案是目前多数的浏览器其实都是多进程的,当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出;每个进程中又有很多的线程,其中包括执行JavaScript代码的线程。
1.2 JavaScript的代码执行是在一个单独的线程中执行的
这就意味着JavaScript的代码,在同一个时刻只能做一件事;如果这件事是非常耗时的,就意味着当前的线程就会被阻塞,所以真正耗时的操作,实际上并不是由JavaScript线程在执行的,是由浏览器的其他线程来完成的,比如网络请求、定时器,我们只需要在特定的时候执行应该有的回调即可。
(1)JavaScript线程执行JS代码,会将异步操作分发给浏览器其他线程进行操作;
(2)然后对异步操作进行分类,划分为微任务队列和宏任务队列;
(3)最后调用栈会对循环队列中的函数进行回调,在调用栈中执行;
那么现在问题来了,我们怎么知道异步操作是属于宏任务还是属于微任务?调用栈在调用循环队列中的函数时,调用的优先级是怎么样的呢?
(1)宏任务队列:ajax、setTimeout、setInterval、DOM监听、UI Rendering等;
(2)微任务队列:Promise的then回调、 Mutation Observer API、queueMicrotask()等;
(1)优先级最高:编写的顶层JS代码,如图中除去setTimeOut函数中的其他代码;
(2)微任务优先级大于宏任务:在执行每个宏任务之前,要先查看微任务队列中是否有微任务需要执行,如果有则先执行微任务;如果没有则执行当前宏任务。
(1)测试题一
- setTimeout(function () {
- console.log("setTimeout1");
- new Promise(function (resolve) {
- resolve();
- }).then(function () {
- new Promise(function (resolve) {
- resolve();
- }).then(function () {
- console.log("then4");
- });
- console.log("then2");
- });
- });
-
- new Promise(function (resolve) {
- console.log("promise1");
- resolve();
- }).then(function () {
- console.log("then1");
- });
-
- setTimeout(function () {
- console.log("setTimeout2");
- });
-
- console.log(2);
-
- queueMicrotask(() => {
- console.log("queueMicrotask1")
- });
-
- new Promise(function (resolve) {
- resolve();
- }).then(function () {
- console.log("then3");
- });
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
我们来画图解析,首先画出三个框分别表示输出值、微任务列表、宏任务列表,顺序都是从上到下,开始都是空的;代码部分内容较多,圈出来使用标签代替;执行玩的部分划掉。如图:
(1)执行第1行,是一个setTimeOut,属于宏任务,所以将part1部分放入宏任务队列;
(2)执行第15行,是一个Promise,函数参数直接执行,所以main script中写入promise1;promise.then()属于微任务,所以将then1放入微任务队列;
(3)执行22行,setTimeOut属于宏任务,将setTimeOut2部分放入宏任务队列;
(4)执行26行,直接输出,main script放入2;
(5)执行28行,queueMicrotask属于微任务,将queueMicrotask1放入微任务队列;
(6)执行32行,promise.then 属于微任务,将then3放入微任务;
此时,直接执行代码已执行完,下面执行微任务队列和宏任务队列,微任务优先级大于宏任务。
(7)执行微任务then1,将then1放入main script;
(8)执行微任务queueMicrotask1,将queueMicrotask1放入main script;
(9)执行微任务then3,将then3放入main script;
此时,微任务队列为空,开始执行宏任务。
(10)执行宏任务part1,将setTimeOut1放入main script;Promise.then属于微任务,将part2放入微任务队列;
此时,微任务中有part2,宏任务中有setTimeOut2,由于微任务优先级大,则执行微任务。
(11)执行微任务part2,Promise.then属于微任务,将part3放入微任务列表;将then2放入main script;
此时,微任务中有part3,宏任务中有setTimeOut2,由于微任务优先级大,则执行微任务。
(12)执行微任务part2,将then4放入main script;
此时,微任务队列为空,开始执行宏任务。
(13)执行setTimeOut2,将setTimeOut2放入main script;
至此,所有代码执行完毕,输出结果顺序为main script中的内容。
(2)测试题二 过程就不画了,可以自己动手画一画
- async function async1() {
- console.log('async1 start')
- await async2();//其后面执行的代码相当于放进then中,作为微任务
- 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')
-
- // script start
- // async1 start
- // async2
- // promise1
- // script end
- // async1 end
- // promise2
- // setTimeout
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。