未加星标

Eventloop的秘密

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

Eventloop的秘密

写在前面

面过前端的小伙伴们都见过这么一道关于异步的小题:

for(var i = 0; i<10; i++){ setTimeout(function(){ console.log(i) },1000) } 复制代码

稍微了解异步的同学都会对答案呼之欲出。BUT! 如果问题升级为:

setTimeout(function(){ console.log(1) },0) new Promise(function executor(resolve) { console.log(2) for(var j = 0;j<100;j++){ j=99&&resolve() } console.log(3) }).then(function(){ console.log(4) }) console.log(5) 复制代码

是不是稍微的有那么点小蒙圈? 别着急,本篇内容结束后以上问题都不再是事儿。解决以上问题的要点,首先需要清楚javascript异步处理模块,事件队列,以及事件环-Eventloop.

基础概念 进程 (process)与线程 (thread)

进程是操作系统分配资源和调度任务的基本单位,线程是建立在进程上的一次程序运行单位,一个进程上可以有多个线程。

进程和线程属基础概念,不再赘述。有个最生动易懂的解释,详情请移步参考: 进程与线程的一个简单解释

Javascript 单线程

对Javascript而言,从诞生之日起,它就是单线程的。为什么呢?举个小栗子:如果可以多线程,a线程要添加某DOM节点,b线程要删除它,浏览器怎么办?难道要精分? 所以说,单线程减少了很多情境的复杂性。

既然js是单线程的,它又以什么样的规则来处理并发的任务呢?千军万马要过独木桥的时候,不能靠力气来抢路。单线程,任务多,就得有个规矩来安排大家。于是秘密终于被我们发现―― 事件环 (Event Loop)就要出马了。 对于首次听说这个概念的同学,有必要铺垫下基础知识:

堆(heap)

对象被分配在一个堆中,即用以表示一个大部分非结构化的内存区域。

栈(stack)

函数调用形成一个栈帧;

栈的特点:先进后出(First in, last out,具体是怎样让那些函数先入后出的?看下图会恍然大明白,图中的帅哥是Philip Roberts,看解释,别光看脸!


Eventloop的秘密
gif图有点大,如果图裂了,请直接看Philip Roberts的演讲: Help, I'm stuck in an event-loop

任务队列(queue)―― 特点:先进先出(FIFO)

一个 JavaScript 运行时包含了一个待处理的消息队列。 每一个消息都有一个为了处理这个消息相关联的函数

说到了任务队列,就到了重点部分:事件环(Eventloop)了

Defined bywebappapis:

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section.

任务队列以事件环来协调事件,用户交互,脚本,渲染,网络等。

事件环 Eventloop Explained byJake Archibald: Each 'thread' gets its own event loop, so each web worker gets its own, so it can execute independently Or explained bysome other guys: This is a constantly running process that checks if the call stack is empty. Imagine it like a clock and every time it ticks it looks at the Call Stack and if it is empty it looks into the Event Queue.

简单来说,每个线程都有他自己的事件环,浏览器也拥有自己的事件环;事件环是一种运行时机制,它像个钟表一样,每滴答一下,就去看看 stack 里有没有事需要处理,没有的话就去事件队列( Event Queue )看看有没有事做。

此处大家需要明白,事件环并不是定死的某个规矩,需要根据不同的运行时进行自己的一套规则。

There are two kinds of event loops: those for browsing contexts, and those for workers.--fromwebapis.

如 node下的事件环 与 浏览器环境下的事件环 就不是相同的规则。一定要记清楚哦!首先讨论浏览器事件环。

浏览器事件环

依据webapis里声明的事件环(event loop)存在时的执行步骤,概括如下:

首先执行script,script被称为全局任务,也属于macrotask; 当macrotask执行完以下,执行所有的微任务; 微任务全部执行完,再取任务队列中的一个宏任务执行。 宏任务包括:script, setTimeout, setInterval, setImmediate, I/O 微任务包括:process.nextTick(node api), 原生Promise(有些实现的promise将then方法放到了宏任务中),Object.observe(已废弃), MutationObserver

一图顶千言,作图解释下:


Eventloop的秘密

看似按照这个规则,我们文章开头的问题就可以有答案了。 回顾一下:

setTimeout(function(){ console.log(1) },0) new Promise(function executor(resolve) { console.log(2) for(var j = 0;j<100;j++){ j=99&&resolve() } console.log(3) }).then(function(){ console.log(4) }) console.log(5) 复制代码

代码执行后,按照执行顺序:

script执行:2,3,5(均为同步任务,new Promise会在同步代码中执行) 处理微任务队列中的所有任务:then 4 接着执行下一个宏任务: setTimeout 1 所以执行结果为: 2,3,5,4,1 微任务

Explained bywebappapis

Each event loop has a microtask queue. A microtask is a task that is originally to be queued on the microtask queue rather than a task queue. There are two kinds of microtasks: solitary callback microtasks, and compound microtasks.

翻译:每个eventloop都有一个微任务队列,微任务最初是被放到微任务队列 而不是任务队列 。

这里大家请把这句话放在心里,这是解决宏任务的一把钥匙所在。

宏任务到底是什么

虽然知道任务队列分为宏任务,微任务,但是一直未找到宏任务的定义,直到看到stackoverflow上的解释。

One go-around of the event loop will have exactly one task being processed from the macrotask queue (this queue is simply called the task queue in the WHATWG specification).

每个事件环必须有一个来自宏任务队列的任务正在执行。 这里将宏任务解释为whatwg上定义的任务队列.

是不是有点迷惑,这不对啊? 虽然把任务队列分成宏任务,微任务理解比较容易,但我认为这后面更大的秘密才是我苦苦找寻的真相。 如果只执行宏任务队列,那微任务队列怎么执行呢?

这正好与上文中提到微任务队列是单独的队列,跟任务队列不是一回事符合。 整个任务可以理解为,所有的宏任务放在一个宏任务队列(即任务队列),处理完一个宏任务(从sccript开始),将微任务队列(包含当时所有的微任务)压入任务队列(宏任务队列)并执行,之后再取下一个任务队列(宏任务)中的宏任务。

于是,我们的题目可以翻译成以下解决思路:

宏任务队列中script执行:2,3,5 微任务队列压入宏任务队列(任务队列)并执行:4 取下一个宏任务执行:1 多说一句

事件环 eventloop 中为什么必须在所有的微任务 microtask 都执行结束后再取新的宏任务 macrotask 呢?

这涉及 microtask 的 执行机制 :


Eventloop的秘密

step2中做了明确的解释,当微任务队列 的标记被写为true之后,只要microtask的队列不为空,eventloop中的当前任务就会按顺序执行microtask队列中的所有任务。

这里可以看到两者的一点区别,微任务 microtask 队列是独立的一个队列,在eventloop执行过程中才进入到任务队列 task queue 一次执行。

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

tags: 队列,console,log,执行,queue,线程,microtask,事件,function,event
分页:12
转载请注明
本文标题:Eventloop的秘密
本站链接:https://www.codesec.net/view/586906.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(27)