资讯专栏INFORMATION COLUMN

js_Event Loop(笔记)

liaoyg8023 / 620人阅读

摘要:单线程的好处简单,处理时不会出现并发竞争问题异步的必要性让用户体验更流畅如何实现异步见参考,,调用栈函数执行上下文。单线程只能有一个并且每次只能执行一个任务。

参考:

JavaScript 运行机制详解:再谈Event Loop

深入理解JavaScript的执行过程--单线程的JS

细说JavaScript单线程的一些事

The JavaScript Event Loop: Explained

模拟Event Loop执行过程

Node.js 事件循环一: 浅析

理解 Event Loop、Micro Task & Macro Task

HTML系列:macrotask和microtask

1. js为啥是单线程执行

单线程是指只在一个线程里执行JS代码,但浏览器是多线程的。

1.1 单线程的好处

简单,处理DOM时不会出现并发竞争问题

2. 异步的必要性

让用户体验更流畅

3. 如何实现异步:Event Loop

见参考1,4,5

3.1 调用栈(call Stack)

函数执行上下文。控制执行函数的调用执行。单线程只能有一个call statck, 并且每次只能执行一个任务。栈是有尺寸的,即在一次执行中函数的嵌套调用数量是有限的,如果超过这个限制就会报错。JS执行时遇到这种错误"Uncaught RangeError: Maximum call stack size exceeded"就是说明call statck溢出了。回调函数上限数量取决于statck本身的大小以及statck元素的大小->见参考:

function computeMaxCallStackSize1() {
    try {
        return 1 + computeMaxCallStackSize1();
    } catch (e) {
        // Call stack overflow
        return 1;
    }
}

// 多了形参p,call stack元素就多占了内存
function computeMaxCallStackSize2(p) {
    try {
        return 1 + computeMaxCallStackSize2();
    } catch (e) {
        // Call stack overflow
        return 1;
    }
}

console.log(computeMaxCallStackSize1()); // 两个输出结果不一样
console.log(computeMaxCallStackSize2());
3.2 回调队列(callback queue)

看参考图,生动的动画模拟可以见参考5。当call stack空的时候,主线程查看callback队列里是否有异步任务,如果有则取出执行,执行完后(即call stack变空了)再去查看call back队列是否有异步任务。这个是循环的过程,也叫事件循环(event loop)。

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

个人认为事件队列主要实现了异步回调功能。

3.3 非阻塞I/O

首先明确一点,JS本身就没有I/O(网络请求,磁盘读写,用户交互)的,所有的I/O操作都是由宿主执行的,宿主提供相关的API供JS调用。当JS调用IO API时并不会等待宿主的执行结果而是继续执行后面的代码,当IO执行完成后宿主再通知JS,即在eventQueue中插入回调task。
不过还有些I/O是同步的,比如同步XHR请求,alert。尽量避免使用。

3.4 阻塞eventLoop

event loop实现了异步回调,但回调只能一个一个的执行,但前面的一个回调非常耗时时就会阻塞后面的回调执行,用户交互也可能出现卡死。

        function sleep(ms) {
            var curr = Date.now();
            while(Date.now() - curr < ms){};
        }
        console.log(1)
        setTimeout(function(){ // 回调依旧被阻塞
            console.log(2) 
        }, 0)
        sleep(2000)

最好开启新线程执行耗时的运算(使用web worker)或者放在服务端运算。

4. Macro-task & micro-task

event loop中除了 callback queue(macro-task)外还有个队列专门处理micro-task。micor-task队列为空时才去处理macro-task队列执行。
当打算以同步的方式处理异步回调(即执行栈为空时立马执行回调函数)时可以采用micro-task方式。

macrotasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering

microtasks: process.nextTick, Promises, Object.observe, MutationObserver

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

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

相关文章

  • Libev 官方文档学习笔记 - 01:概述和 ev_loop

    摘要:注意这个函数并不是线程安全的。等于新事件的数量。暂停当前的,使其刮起当前的所有工作。两个值均默认为,表示尽量以最小的延迟调用。系列篇官方文档学习笔记概述和本文官方文档学习笔记基础官方文档学习笔记常用接口使用构建响应服务器的简单流程 请注意这是 libev 而不是 libevent 的文章! 自从接触到 libev 之后,就深深赞同作者精简的设计理念,于是就爱上了 libev 这样简单的...

    codeKK 评论0 收藏0
  • Libev 官方文档学习笔记 - 02:watcher 基础

    摘要:回调可在这两个中开始停止相应的。即将被销毁已经被异步通知不是发送的信号。此外还需要调用相应的函数。停止,并清空状态。当且仅当时返回。系列篇官方文档学习笔记概述和官方文档学习笔记基础本文官方文档学习笔记常用接口使用构建响应服务器的简单流程 请注意这是 libev 而不是 libevent 的文章! 这篇文章是第二篇,主要讲 libev 里的 watcher 的一些基础操作。 本文地址:h...

    yimo 评论0 收藏0
  • Libev 官方文档学习笔记 - 03:常用 watcher 接口

    摘要:示例从中进行相对超时机制提供了一个相对超时机制的定时器。所谓的相对,就是说这个定时器的参数是指定以当前时间为基准,延迟多久出发事件。这个非常便于用来提供诸如下一个正午点之类的定时器。 请注意这是 libev 而不是 libevent 的文章! 这篇文章是第三篇,主要讲 libev 里基本集中的 watcher。 本文地址:https://segmentfault.com/a/11900...

    Carson 评论0 收藏0
  • 使用 libev 构建 TCP 响应服务器(echo server)的简单流程

    摘要:事件同理准备用来的读取的方法就视乎程序员的实现啦把数据回去原文例子使用的就是,实际上我个人偏爱的是启动这里可以直接使用系列篇官方文档学习笔记概述和官方文档学习笔记基础官方文档学习笔记常用接口使用构建响应服务器的简单流程本文 请注意这是 libev 而不是 libevent 的文章!这篇文章主要是使用具体的例子,说明如何使用 libev。网上不少文章都是照抄示例,一点用都没有……本文将示...

    ymyang 评论0 收藏0
  • 06.Android之消息机制问题

    摘要:通过向消息池发送各种消息事件通过处理相应的消息事件。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。 目录介绍 6.0.0.1 谈谈消息机制Hander作用?有哪些要素?流程是怎样的? 6.0.0.2 为什么一个线程只有一个Looper、只有一个MessageQueue,可以有多个Handle...

    Aomine 评论0 收藏0

发表评论

0条评论

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