Javascript 运行机制详解,Event Loop

javascript的运行机制,Event Loop

单线程

javascript为什么是单线程语言,原因在于如果是多线程,当一个线程对DOM节点做添加内容操作的时候,另一个线程要删除这个DOM节点,这个时候,浏览器应该怎么选择,这就造成了混乱,为了解决这类问题,在一开始的时候,javascript就采用单线程模式。

在后面H5出的web worker标准的时候,看似是多线程,其实是在一个主线程来控制其他线程,而且不能操作DOM,所以本质还是单线程

任务队列

任务可以分为两种,一种为同步,另一种为异步(具有回调函数)。如下图:

eventloop.png

所有的同步任务都在主线程上执行,形成一个执行栈 stack。当所有同步任务执行完毕后,它会去执行microtask queue中的异步任务(nextTick,Promise),将他们全部执行。主线程之外还有一个任务队列task queue,当有异步任务(DOM,AJAX,setTimeout,setImmediate)有结果的时候,就在任务队列里放一个事件,一旦执行栈和microtask queue任务执行完毕,系统就会读取任务队列,将取出排在最前面的事件加入执行栈执行,这种机制就是任务队列。

Event Loop

主线程在任务队列中读取事件,这个过程是循环不断地,所以这种运行机制叫做Event Loop(事件循环)

nextTick、setImmediate、setTimeout

nextTick是在执行栈同步代码结束之后,下一次Event Loop(任务队列)执行之前。当所有同步任务执行完,会在queue中执行nextTick,无论nextTick有多少层回调,都会执行完毕后再去任务队列,所以会造成一直停留在当前执行栈,无法执行任务队列,请看下面代码

process.nextTick(function () {
    console.log('nextTick1');
    process.nextTick(function (){console.log('nextTick2')});
});
  
setTimeout(function timeout() {
    console.log('setTimeout');
}, 0)

执行完毕后输出nextTick1、nextTick2、setTimeout,原因是nextTick是在当前执行栈末尾执行,而setTimeout是在下次任务队列在执行

setImmediate方法是在Event Loop(任务队列)末尾,也就是下一次Event Loop时执行。

setTimeout方法是按照执行时间,放入任务队列,有时快与setImmediate有时慢。请看以下代码

setImmediate(function () {
    console.log('setImmediate1');
    setImmediate(function (){console.log('setImmediate2')});
});
  
setTimeout(function timeout() {
    console.log('setTimeout');
}, 0);

这段代码执行完可能是setImmediate1、setTimeout、setImmediate2,也可能是setTimeout、setImmediate1、setImmediate2,原因是setTimeout和setImmediate1都是在下次Event Loop中触发,所以先后不确定,但是setImmediate2肯定是最后,因为他是在setImmediate1任务队列之后,也就是下下次Event Loop执行

Node.js的Event Loop

Node.js也是单线程的Event Loop但是和浏览器有些区别,如图所示,

1.先通过Chrom V8引擎解析Javascript脚本

2.解析完毕后调用Node API

3.LIBUV库负责Node API的执行,将不同任务分配给不同的线程,形成一个Event Loop(任务队列)

4.最后Chrom V8引擎将结果返回给用户

nodesystem.png

Node.js Event Loop原理

node.js的特点是事件驱动,非阻塞单线程。当应用程序需要I/O操作的时候,线程并不会阻塞,而是把I/O操作交给底层库(LIBUV)。此时node线程会去处理其他任务,当底层库处理完I/O操作后,会将主动权交还给Node线程,所以Event Loop的用处是调度线程,例如:当底层库处理I/O操作后调度Node线程处理后续工作,所以虽然node是单线程,但是底层库处理操作依然是多线程

Node Event Loop的事件处理机制

   ┌───────────────────────┐

┌─>│        timers         │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │     I/O callbacks     │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │     idle, prepare     │

│  └──────────┬────────────┘      ┌───────────────┐

│  ┌──────────┴────────────┐      │   incoming:   │

│  │         poll          │<─────┤  connections, │

│  └──────────┬────────────┘      │   data, etc.  │

│  ┌──────────┴────────────┐      └───────────────┘

│  │        check          │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

└──┤    close callbacks    │

   └───────────────────────┘

上面处理阶段都是按照先进先出的规则执行回调函数,按顺序执行,直到队列为空或是该阶段执行的回调函数达到该阶段所允许一次执行回调函数的最大限制后,才会将操作权移交给下一阶段。

  • timers: 用来检查setTimeout()和setInterval()定时器是否到期,如果到期则执行它,否则下一阶段
  • I/O callbacks: 用来处理timers阶段、setImmediate、和TCP他们的异常回调函数或者error
  • idle, prepare: nodejs内部函数调用,在循环被I/O阻塞之前prepare回调就会立即调用
  • poll: 用来监听fd的事件的,比如socket的可读,可写,文件的可读可等等
  • check: setImmediate()函数只会在这个阶段执行
  • close callbacks: 执行一些诸如关闭事件的回调函数,如socket.on('close', ...)

具体分析,看下图:

1.当setTimeout时间最小,读取文件不存在的时候

eventloop-setTimeout0-unfile.png

如图所示,分别是nextTick、readFile、setTimeout、setImmediate,然而现在并没有1.txt和2.txt文件,输出结果是next Tick、setTimeout、readFile、setImmediate,在event loop中先判断的是timeers,最先出书next Tick因为process.nextTick的实现是基于v8 MicroTask(是在当前js call stack 中没有可执行代码才会执行的队列,低于js call stack 代码,但高于事件循环,不属于Event Loop,上面javascript的Event Loop介绍过了,所以最先输出。然后开始走Event Loop,第一阶段是timers,判断setTimeout到期,所以输出setTimeout,进入下一阶段,poll将I/O操作权交出,新线程操作,但是并没有相关读取文件,所以直接返回回调函数,所以处处readFile,最后到check阶段,输出setImmediate

2.当setTimeout时间最小,读取文件存在的时候

eventloop-setTimeout0-file.png

如图所示,分别是nextTick、setTimeout、setImmediate、readFile,这次readFile在最后面,是因为文件存在,执行到poll阶段的时候,执行I/O操作,node线程开始执行check阶段,当交出的I/O操作结束后,返回给Event Loop所以再执行readFile的回调函数,所以他在最后面

3.当setTimeout时间为100毫秒,读取文件不存在的时候

eventloop-setTimeout100-unfile.png

如图所示,分别是nextTick、readFile、setImmediate、setTimeout,它和1不同的地方是setTimeout排在最后了,这是因为在执行timers的时候,setTimeout没有到期,所以直接执行下一阶段,当执行完poll的时候,会去执行查看定时器有没有到期,如果没有下一次Event Loop再次查看,知道定时器到期,所以他在最后面

4.当setTimeout时间为100毫秒,读取文件存在的时候

eventloop-setTimeout100-file.png

如图所示,分别是nextTick、setImmediate、readFile、setTimeout,它和2的区别是setTimeout在最后,原因和3一样。

总结一下

1.javascript和node.js都是单线程,但是node底层是多线程操作

2.Event Loop —— 任务队列

3.当同时设置nextTick, setImmediate, setTimeout时一定是nextTick先执行,nextTick不属于Event LOop,它属于v8的micro tasks,并且会阻塞Event Loop

4.setImmediate,setTimeout属于Event Loop但是,直接阶段不同

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

推荐阅读更多精彩内容