直接搞定浏览器js运行机制和事件循环机制

写在前头

关于浏览器Event Loop我发现有很多到文章都没有说清楚,包括有很多小伙伴都感觉云里雾里。有的文章说宏任务是先于微任务执行,有的说是微任务先于宏任务执行,实际上这种矛盾就是因为前提没有说清楚,才会导致概念和代码运行打印不同。带着这个疑问,我们一起来探究,这篇文章一定会让你完全掌握浏览器的事件循环机制。

为什么要事件循环?

因为js是单线程,单线程意味着每次只能执行一个任务,如果遇到前面一个任务耗时很长,后面的任务就不得不一直等着,这样会造成阻塞,为了协调事件、用户交互、脚本、渲染和网络等,必须使用事件循环。

任务执行顺序?

js执行的任务有两种,一种是同步任务,一种是异步任务。

同步任务:会立即执行的任务

异步任务:不会立即执行的任务(异步任务又分为宏任务与微任务)。常见的异步任务:Ajax,Dom事件操作,setTimeOut,promise的then方法,Node读取文件

宏任务包括,script(整体代码), setTimeout, setInterval, setImmediate

微任务包括,process,nextTick(Nodejs), Promises.then,用户触发的点击事件,滚动事件,键盘事件


javascript的运行机制:

1、所有同步任务都在主线程上执行,形成一个“执行栈”

2、主线程之外还存在一个“任务队列”,当事件被识别为异步任务的时候,浏览器会把这个任务放在 Event Table,这个Event Table会把传入的异步事件注册为一个回调函数,然后传给Event Queue,等待主线程执行。

3、那什么时候这个回调函数会被执行呢?

在js引擎中,存在一个monitoring process的进程,它会不断检查主线程的任务是否全部执行完成,一旦全部执行完成,就会去任务队列(Event Quene)检查有哪些待执行的函数。如果有,就把这个回调函数放在主线程上执行

4、而这个异步任务不断排队,主线程不断检查异步排队,主线程不断执行的循环就叫事件循环 Event Loop

需要注意的是:当前执行栈执行完毕时会立刻先处理掉微任务队列中的事件,然后再去宏任务取事件。当执行宏任务时,此刻微任务队列有新的事件进来,会先把其他宏任务放一边,先执行完微任务队列里的任务,再继续执行宏任务。

在一个任务队列中,可以有多个的宏任务队列,但是微任务队列只有一个.

同一次事件循环中,微任务优先于宏任务执行。前提是同一次事件循环中。

这句话是本章的重点。事实上我们可以看到很多文章都是说宏任务优先于微任务执行,这是对的。然而很多例子显示却不是如此,下面是正确理解的执行步骤。

1、从全局的script开始,任务依次入栈,遇到同步事件直接被主线程执行,若执行完则出栈,这包括promise事件,因为它是立即执行。遇到异步事件就放到任务队列区分宏任务和微任务

2、第一次同步任务执行完后,在本轮看看微任务有没有任务,有就执行,没有就进入下一轮执行宏任务

3、执行的宏任务里如果有微任务,就把其他宏任务放一边,先执行微任务

所以宏任务是先于微任务执行指的就是我们第一次运行的整体代码就是宏任务!接下来的每次循环都是微任务优先于宏任务!!

Promise的回调函数不是正常的异步任务,而是微任务(microtask)。区别在于,正常异步任务追加到下一轮事件循环,微任务追加到本轮事件循环。js中的代码执行顺序为:同步程序——promise微任务——异步程序。下面我们直接上代码演示,然后在下一章,我会仔细讲解ES6的promise、async和await的特点和用法,另外准备一些面试题给大家学习。

例子1:

            console.log('1');

            setTimeout(function() {

                console.log('2');

                new Promise(function(resolve) {

                    console.log('4');

                    resolve();

                }).then(function() {

                    console.log('5')

                })

            })

            new Promise(function(resolve) {

                console.log('7');

                resolve();

            }).then(function() {

                console.log('8')

            })

            setTimeout(function() {

                console.log('9');

                new Promise(function(resolve) {

                    console.log('11');

                    resolve();

                }).then(function() {

                    console.log('12')

                })

            })

第一轮循环

1)、首先打印 1

2)、接下来是setTimeout是异步任务且是宏任务,加入宏任务暂且记为 setTimeout1

3)、接下来是 process 微任务 加入微任务队列 记为 process1

4)、接下来是 new Promise 里面直接 resolve(7) 所以打印 7 后面的then是微任务 记为 then1

5)、setTimeout 宏任务 记为 setTimeout2

第一轮循环打印出的是 1 7 8(这个8在本轮同步任务结束后执行微任务)

当前宏任务队列:setTimeout1, setTimeout2

当前微任务队列:then1(本轮结束前执行所有微任务)

第二轮循环:

1)、微任务都执行结束了,开始执行第一个宏任务

2)、执行 setTimeout1 (第一个setTimeout)

3)、首先打印出 2

4)、new Promise中resolve 打印出 4

5)、then 微任务 记为 then2

当前宏任务队列:setTimeout2

当前微任务队列:then2(本轮结束前执行所有微任务)

第二轮循环结束,当前打印出来的是 1 7 8 2 4 5

第三轮循环:

1)、执行第一个宏任务,也就是执行 setTimeout2 (第二个setTimeout)

2)、首先打印出 9

3)、new Promise执行resolve 打印出 11

4)、then 微任务 记为 then3

当前宏任务队列为空

当前微任务队列:then3(本轮结束前执行所有微任务)

第三轮循环结束,最终打印出来的是 1 7 8 2 4 5 9 11 12

例子2:

console.log('第一轮');

        setTimeout(() => {                    //为了便于叙述时区分,标记为 setTimeout1

            console.log('第二轮');

            Promise.resolve().then(() => {    //为了便于叙述时区分,标记为 then1

                console.log('A');

            })

        }, 0);

        setTimeout(() => {                    //为了便于叙述时区分,标记为 setTimeout2

            console.log('第三轮');

            console.log('B');

        }, 0);

        new Promise((resolve)=>{              //为了便于叙述时区分,标记为 Promise1

            console.log("C")

            resolve()

        }).then(() => {                      //为了便于叙述时区分,标记为 then2

            Promise.resolve().then(() => {    //为了便于叙述时区分,标记为 then3

                console.log("D")

                setTimeout(() => {            //为了便于叙述时区分,标记为 setTimeout3

                    console.log('第四轮');

                    console.log('E');

                }, 0);

            });

        });

// 执行结果:第一轮 > C > D > 第二轮 > A > 第三轮 > B > 第四轮 > E


        第一轮

        宏任务:setTimeout1,setTimeout2,setTimeout3

        微任务:then2,then3 (第一轮结束前清空微任务)

        打印:第一轮,C,D

        上面这里要注意,第一轮中微任务队列不为空,then2出队列并执行,然后这个微任务里的 then3继续进入微任务队列,只要微任务队列不为空,本轮就没有结束。执行then2的时候,then3和setTimeout3分别入列

        第二轮

        宏任务:setTimeout2,setTimeout3

        微任务:then1(第一轮结束前清空微任务)

        打印:第二轮,A

        第三轮

        宏任务:setTimeout3

        微任务:

        打印:第三轮,B,

        第四轮

        宏任务: 空

        微任务:空

        打印:第四轮,E

大家看看自己能否真正理解。不理解可以留言,我会解答并增加例子解释。这篇文章更正了很多文章的错误,同时集结其他文章的突出地方。下一篇讲promise,更多详细案例等你。

参考链接:

https://zhuanlan.zhihu.com/p/257069622

https://segmentfault.com/a/1190000018181334

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容