关于JS事件循环的理解

之前一直有疑惑过像elm.addEventListener('click', fn)这样的一行代码怎么就能在用户点击目标元素的时候执行fn()?今天看了些网上关于“事件循环”、“异步原理”的文章,大体明白了怎么回事,当然也只是大体,毕竟我确定我将要记的这些笔记是模棱两可的,因为还有些东西暂时没弄明白,但又不想像往常一样预想等到完全弄明白时再记而导致最终连好不容易弄明白那些的都慢慢又忘了,所以还是硬着头皮梳理一下。

先从一个问题开始:

  • 为什么JS是单线程语言?
    直接贴阮一峰的文章内容:

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

这个回答整体来看是能说服我的,JS单线程的是由于它的用途。但是第一句话一开始我是没明白的,他说“JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。”同一时间只能做同一件事,这里面只能做一件事的是谁?网上很多文章都这样,一句话下来总是要少点什么,比如这里的主语。当然,对于一下就能知道这里面的主语是谁的那类读者,省略了也无大碍,但对于其他类的读者,可能就加大了读这篇文章的困难。所以我花了一些时间,猜测这里的主语应该是“JS引擎”。

A JavaScript engine is a program or interpreter which executes JavaScript code

JavaScript引擎是一个专门处理JavaScript脚本的虚拟机

英文的是维基百科上的定义,中文的是百度百科的定义,二者应该等价吧?可能维基的更严谨一点?好吧,我是打算用百度百科的定义的,虽然我也不知道“虚拟机”(维基百科和百度百科的定义也不一样)是什么,只知道大学同学用通过虚拟机在电脑上装了两个系统,所以我想对于JS引擎来讲,不求它装系统,内部自己管理自己的线程应该不成问题吧?(突然觉得通过一个定义来理解一个东西这件事也很难,因为没有统一的标准,每个下定义的人只用负责自己下的定义在自己的理论里自圆其说就可以了)。算了,我也不管了,不然这点笔记怕是开始都开始不了,直接瞎说八道吧。

我的理解是这样的:JS这门编程语言本身是没有所谓单线程、多线程之分的,但是一直以来JS引擎是单线程的,所以我们就称JS语言是单线程。换句话说如果JS一开始用于另外一种用途,人们实现的JS引擎是多线程的,那么我们今天可能就会说JS语言是多线程。如果我这样理解没错的话(为避免麻烦后面的内容就不加这句话了),那“JS语言是单线程的”这种说法多少有点不严谨。

这样一来,就好理解了,“JS引擎是单线程的”这句话是说JS引擎只有一个线程来处理JS代码,所以它在同一时间只能做同一件事。

关于线程:只能说大学里没逃的那一半操作系统课上老师教的东西我都还给她了,当时就没弄懂线程和进程到底是什么,现在也不懂(后面会懂的,今天看文章的经历看来,有必要重新系统学习一下这方面的知识了),不过还好,在这里暂时也没多大影响,前面和后面的用到线程的地方就算是错的应该也不影响整体的理解。

然后下一个问题:

  • javascript既然是单线程语言 , 为什么会分主线程和消息线程(event loop) ?

单线程是对于JS引擎内部来讲,因为JS引擎内部只维持一个线程所以没有主次之分;然而对外来讲,JS引擎自己本身也只不过是其宿主环境的一个线程。所以硬要说主线程的话,对于宿主环境来讲,可以指JS引擎本身(JS引擎是不是宿主环境的主线程我也不知道),但是如果问题里的主线程是想指JS引擎里的那个唯一的进程,那又是有欠严谨的。

笔记主要内容,先上图:

image.png

这是JavaScript 运行机制详解:再谈Event Loop--阮一峰这篇文章应用的外国一个人的演讲里的图,这里的宿主环境是浏览器,可以看到JS引擎只是浏览器的几个线程之一。JS引擎内部的线程只做一件事,就是按顺序执行每一行JS代码,如果碰到像DOM事件绑定、ajax请求、setTimeout等这样涉及到异步操作的Web APIs时,JS引擎就把它们交给浏览器内核的其他模块进行处理,webkit内核在Javasctipt执行引擎之外,有一个重要的模块是webcore模块。对于图中WebAPIs提到的三种API,webcore分别提供了DOM Binding、network、timer模块来处理底层实现。这些模块处理完这些操作的时候便将回调函数放入任务队列中。当stack为空的时候,JS引擎就会去callback queue里取出队头的callback放到statck里执行,等到stack再次变空之后又继续重复刚才的操作。JS引擎从callback queue里取出callback放到stack去执行这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

不知道为啥叫事件循环,是因为这些回调函数的执行都可以看成是事件驱动的么?不管实实在在的鼠标点击事件还是ajax请求还是计时器都可以把它看成事件?所以,JS异步的本质上都是通过触发某种条件然后去执行对应的回调函数而实现。

来个例子:

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

按理明明setTimeout()里的console.log(4)应该是在callback queue的队首的,也就是说它的执行应该要排在.then()里的console.log(5)的前面的,而最后的log顺序是12354,然后知道,原来对于图中的callback queue实际上不止一个的,具体见:http://www.jianshu.com/p/12b9f73c5a4f
https://zhuanlan.zhihu.com/p/26229293

现在可以解答elm.addEventListener('click', fn)这样的一行代码怎么就能在用户点击目标元素的时候执行fn()的问题了:
JS引擎内部的线程执行到这一行代码的时候,发现它是要进行异步操作的,JS引擎便把它交给浏览器对应的DOM Binding模块处理,从那时起这个模块便开始监听elm这个元素的点击事件,一旦发生这个事件便把对应的fn放到callback queue里,然后这个callback就静静的等待JS引擎把它放到stack里面执行。

大概原理就是这些,细节的后面碰到不明白的再说。

图里的heap涉及到js内存空间的知识,stack涉及到执行上下文(Execution context)和函数调用栈(call stack)的知识,后面完全弄清楚了再说。

碰到疑惑然后想法子去解决固然是好的,但是也仅限于解决,解决之后就很少继续深入了,给自己借口是:要是学了没用上很快就忘了之后又要再重新学一遍怎么办?不知道这种学习方式有无问题。不过无论如何,输入之后再输出总是没错的。

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

推荐阅读更多精彩内容