资讯专栏INFORMATION COLUMN

深入理解 Event Loop

Carbs / 2017人阅读

摘要:即使现在支持,由于没有多线程的机制,和执行线程只能通过来通信,而且由于没有锁,无法访问和对象。的单线程是指一个浏览器进程中只有一个的执行线程,即同一时刻内只会有一段代码在执行。与单线程如何实现异步设计了一个事件循环的方式。

众所周知,JavaScript(以下简称 JS) 是单线程语言,在 html5 中增加了 web workers,web workers 是新开了线程执行的,那么 JS 还是单线程的吗?当然是,为什么要设计成单线程?

网上有很多说法,大部分都说是多个线程同时对一个dom操作(同时修改dom内容,一个线程增加属性,一个线程删除属性),会非常混乱,当然如果支持多线程就会相应的就要加入多线程的锁机制,那么 JS 就变得非常复杂了,想想 JS 最开始设计的初衷就是用于用户交互,而且当时的原始需求是:功能不需要太强,语法较为简单,容易学习和部署,Brendan Eich 只用了10天,就设计完成了这种语言的第一版,因此也不可能加入多线程这么复杂的技术。

即使现在支持 web workers,由于没有多线程的机制,web workers 和执行线程只能通过 postMessage 来通信,而且由于没有锁,web workers 无法访问 window 和 document 对象。

JS 的单线程是指一个浏览器进程中只有一个 JS 的执行线程,即同一时刻内只会有一段代码在执行。

Micro-Task 与 Macro-Task

单线程如何实现异步?JS 设计了一个事件循环的方式。所有的代码执行均按照事件循环的方式进行。

事件循环中分两种任务:一个是宏任务(Macro-Task),另一个是微任务(Micro-Task)。常见的宏任务和微任务如下。

宏任务:script(整体代码)、setTimeout、setInterval、requestAnimationFrame、I/O、事件、MessageChannel、setImmediate (Node.js) 微任务:Promise.then、 MutaionObserver、process.nextTick (Node.js)

事件循环按下图的方式进行。

注意: 宏任务执行完后,需要清空当前微任务队列后才回去执行下一个宏任务,如果微任务里面产生了新的微任务,仍然会在当前事件循环里面被执行完,后面会举例说明。

来个示例验证下上面的流程。

<script>
    console.log(1);

    setTimeout(function timeout1() {
        console.log(2);
    }, 0);

    Promise.resolve().then(function promise1() {
        console.log(3);
        setTimeout(function timeout2() {
            console.log(4);
            Promise.resolve().then(function promise2() {
                console.log(5);
            });
        }, 0);
        return Promise.resolve()
            .then(function promise3() {
                console.log(6);
                return Promise.resolve().then(function promise4() {
                    console.log(7);
                });
            })
            .then(function promise5() {
                console.log(8);
            });
    })
    console.log(9);
script>

<script>
    console.log(10);
    setTimeout(function timeout3() {
        console.log(11);
    }, 0);
    Promise.resolve().then(function promise6() {
        console.log(12);
    });
script>

按照上面流程梳理下执行流程:

    将两个宏任务(两个script代码)初始化进宏任务队列,宏任务队列为:[script1, script2]

    script1 出队压入执行栈执行,宏任务队列为:[script2]

    同步代码执行输出:1,

    timeout1 入队,宏任务队列为:[script2, timeout1]

    promise1 入队,微任务队列为:[promise1]

    同步代码执行输出:9

    script1 执行完毕,进入微任务执行阶段,promise1 出队压入执行栈执行,微任务队列为空

    同步代码执行输出:3

    timeout2 入队,宏任务队列为:[script2, timeout1, timeout2]

    promise3 入队,微任务队列为:[promise3]

    promise1 执行完毕,继续判断微任务队列是否为空,promise3 出队压入执行栈执行,微任务队列为空

    同步代码执行输出:6

    promise4 入队,微任务队列为:[promise4]

    promise3 执行完毕,promise5 入队,微任务队列为:[promise4,promise5]

    判断微任务队列是否为空,promise4 出队压入执行栈执行,微任务队列为:[promise5]

    同步代码执行输出:7

    promise4 执行完毕,继续判断微任务队列是否为空,promise5 出队压入执行栈执行,微任务队列为空

    同步代码执行输出:8

    微任务队列清空,宏任务 script2 出队压入执行栈执行,宏任务队列为[ timeout1, timeout2]

    同步代码执行输出:10

    timeout3 入队,宏任务队列为:[timeout1, timeout2, timeout3]

    promise6 入队,微任务队列为:[promise6]

    script2 执行完毕,进入微任务执行阶段,promise6 出队压入执行栈执行,微任务队列为空

    同步代码执行输出:12

    微任务队列为空,执行宏任务队列,timeout1 判断是否到时间,timeout1 到时压入执行栈执行,宏任务队列为[ timeout2, timeout3]

    同步代码执行输出:2

    微任务队列为空,执行宏任务队列,timeout2 判断是否到时间,timeout2 到时压入执行栈执行,宏任务队列为[ timeout3]

    同步代码执行输出:4,promise2 入队,微任务队列为:[promise2]

    timeout2 执行完毕,判断微任务队列是否为空,promise2 出队压入执行栈执行,微任务队列为空

    同步代码执行输出:5

    微任务队列为空,执行宏任务队列,timeout3 判断是否到时间,timeout3 到时压入执行栈执行,宏任务队列为空

    同步代码执行输出:11

    宏任务队列为空

参考

JavaScript语言的历史

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/6834.html

相关文章

  • 深入理解js引擎的执行机制

    摘要:深入理解引擎的执行机制最近在反省,很多知识都是只会用,不理解底层的知识。在阅读之前,请先记住两点是单线程语言的是的执行机制。所以,是存在异步执行的,比如单线程是怎么实现异步的场景描述通过事件循环,所以说,理解了机制,也就理解了的执行机制啦。 深入理解js引擎的执行机制 最近在反省,很多知识都是只会用,不理解底层的知识。所以在开发过程中遇到一些奇怪的比较难解决的bug,在思考的时候就会收...

    feng409 评论0 收藏0
  • 10分钟理解JS引擎的执行机制

    摘要:深入理解引擎的执行机制灵魂三问为什么是单线程的为什么需要异步单线程又是如何实现异步的呢中的中的说说首先请牢记点是单线程语言的是的执行机制。 深入理解JS引擎的执行机制 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.说说setTimeout 首先,请牢记2...

    zzbo 评论0 收藏0
  • JavaScript执行机制、事件循环

    摘要:曾经的理解首先,是单线程语言,也就意味着同一个时间只能做一件事,那么为什么不是多线程呢这样还能提高效率啊假定同时有两个线程,一个线程在某个节点上编辑了内容,而另一个线程删除了这个节点,这时浏览器就很懵逼了,到底以执行哪个操作呢所以,设计者把 Event Loop曾经的理解 首先,JS是单线程语言,也就意味着同一个时间只能做一件事,那么 为什么JavaScript不是多线程呢?这样还能提...

    rose 评论0 收藏0
  • 【转】深入理解JS单线程机制【原文作者:MasterYao】

    摘要:的单线程,与它的用途有关。只要指定过回调函数,这些事件发生时就会进入任务队列,等待主线程读取。四主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环。令人困惑的是,文档中称,指定的回调函数,总是排在前面。 原文:http://www.cnblogs.com/Master... 一、为什么JavaScript是单线程? JavaScript语言的一大特点...

    LittleLiByte 评论0 收藏0
  • 由setTimeout深入JavaScript执行环境的异步机制

    摘要:图片转引自的演讲和两个定时器中回调的执行逻辑便是典型的机制。异步编程关于异步编程我的理解是,在执行环境所提供的异步机制之上,在应用编码层面上实现整体流程控制的异步风格。 问题背景 在一次开发任务中,需要实现如下一个饼状图动画,基于canvas进行绘图,但由于对于JS运行环境中异步机制的不了解,所以遇到了一个棘手的问题,始终无法解决,之后在与同事交流之后才恍然大悟。问题的根节在于经典的J...

    codeGoogle 评论0 收藏0
  • Javascript事件循环入门到“忘记”(一).md

    摘要:中叫做调用栈先进后出,后进先出。如下图这是典型的内存溢出,可能会出现在某些场景下需要递归,但业务逻辑中的判断又没能正常计算进入到预设情况,于是调用栈中不断进入,又无法执行完,就造成内存溢出了。 本文主要介绍Javascript事件循环在浏览器上的一些特性和应用介绍。 Javascript小知识 JavaScript的并发模型基于事件循环(Event Loop)。这个模型与像C或者Jav...

    codeGoogle 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<