目录
浏览器UI多线程
js单线程
执行栈
任务队列
事件与回调
事件循环
实例
首页 web前端 js教程 浏览器UI多线程及对JavaScript单线程底层运行机制的理解

浏览器UI多线程及对JavaScript单线程底层运行机制的理解

Feb 28, 2017 pm 02:37 PM
javascript 运行机制

早在我刚刚学习JavaScript的时候,我就被“灌输”了这样的思想 

JavaScript是单线程的
可是在我不断的学习过程中
学到了定时器、ajax的异步加载
一度让我对这句话产生怀疑
既然JavaScript是单线程的,为什么它还存在异步加载?
后来我知道了浏览器中不仅仅有js一个线程,它与其他线程共同组成了 —– 浏览器UI多线程

早就想写一篇这样的文章,只是感觉理解的还不够,怕我写的对大家产生误导
看完网上大家写的各种博文,更是一脸懵逼
不过最后还是鼓起勇气决定来谈一下╮(╯_╰)╭
那么现在抛开JavaScript线程的问题不谈,我们先来看看什么是浏览器UI线程

浏览器UI多线程

先来上一张图

浏览器UI多线程

一般情况下浏览器至少有三个常驻线程

  • GUI渲染线程(渲染页面)

  • JS引擎线程(处理脚本)

  • 事件触发线程(控制交互)

包括浏览器有时候会开辟新的线程比如用完就扔的Http请求线程(Ajax)等等其他线程
它们共同构成了浏览器的UI线程
这些线程在UI线程的控制下井然有序的工作着
关于这个常驻线程网上观点不一致,各个浏览器实现可能也不一样,这里就不深考究了

虽然我把js引擎线程放在了右下角,但是它是浏览器的主线程
而且它和GUI渲染线程是水火不容的,不能够同时工作
道理很简单,因为它们都要操作DOM,如果js线程想要某个DOM的样式,渲染引擎必须停止工作(霸道总裁让你站那儿别动)

js单线程

为什么JavaScript是单线程
单线程就是同一时间只能干一件事
那么JavaScript多线程不好吗,那效率多高啊
不好
js设计出来就是为了与用户交互,处理DOM
如果JavaScript多线程了,那就必须处理多线程同步的问题(依稀记得曾经被C++线程同步支配的恐怖)
假如js是多线程,同一时间一个线程想要修改DOM,另一个线程想要删除DOM
问题就变得复杂许多,浏览器不知道听谁的,如果引入“锁”的机制,那就麻烦死了(那我就不学前端了( ̄_, ̄ ))
所以我们这样一个脚本语言根本没有必要搞得那么复杂,所以JavaScript诞生起就是单线程执行

虽然H5提出了Web Worker,但是它不能够操作DOM,还是要委托个大哥js主线程解决
这个Web Worker我还没有了解太多,这里就不多说了
这些子线程完全受主线程大哥控制的,所以没有改变JavaScript单线程的本质

执行栈

我们先来看看什么是执行栈
栈是先进后出(FILO)的数据结构
执行栈中存放正在执行的任务,每一个任务叫做“帧”
举个例子

function foo(c){
    var a = 1;
    bar(200);
}function bar(d){
    var b = 2;
}
foo(100);
登录后复制

我们来看看执行栈发生了怎样的变化

  • 最开始,代码没有执行的时候,执行栈为空栈

  • foo函数执行时,创建了一帧,这帧中包含了形参、局部变量(预编译过程),然后把这一帧压入栈中

  • 然后执行foo函数内代码,执行bar函数

  • 创建新帧,同样有形参、局部变量,压入栈中

  • bar函数执行完毕,弹出栈

  • foo函数执行完毕,弹出栈

  • 执行栈为空

执行栈其实相当于js主线程

任务队列

队列是先入先出(FIFO)的数据结构
js线程中还存在着一个任务队列
任务队列包含了一系列待处理的任务
单线程就意味着所有任务需要一个接一个的执行,如果一个任务执行的时间太长,那后面的任务就不得不等着
就好比护士阿姨给排队的小朋友打针,如果最前面的小朋友一直滚针,那就一直扎,后面的小朋友就得等着(这比喻好像不恰当)
可是如果最前面的小朋友晕针昏倒了
那么护士阿姨不可能坐那里了等到他醒来,一定是先给后面的小朋友扎针
也就是相当于把那位小朋友“挂起”(异步)

所以,任务可以分为两种

  • 同步任务

  • 异步任务

同步任务就是正在主线程执行栈中执行的任务(在屋子内打针的小朋友)
而异步任务是在任务队列等候处理的任务(在屋子外等候打针的小朋友)
一旦执行栈中没有任务了,它就会从执行队列中获取任务执行

事件与回调

任务队列是一个事件的队列,IO设备(输入/输出设备)每完成一项任务,就会在任务队列中添加事件处理
用户触发了事件,也同样会将回调添加到任务队列中去
主线程执行异步任务,便是执行回调函数(事件处理函数)
只要执行栈一空,排在执行队列前面的会被优先读取执行,
不过主线程会检查时间,某些事件需要到了规定时间才能进入主线程处理(定时器事件)

事件循环

主线程从执行队列不断地获取任务,这个过程是循环不断地,叫做“Event Loop”事件循环
同步任务总是会在异步任务之前执行
只有当前的脚本执行完,才能够去拿任务队列中的任务执行
前面也说到了,任务队列中的事件可以是定时器事件
定时器分为两种 setTimeout() 和 setInterval()
前者是定时执行一次,后者定时重复执行
第一个参数为执行的回调函数,第二个参数是间隔时间(ms)
来看这样一个例子

setTimeout(function(){
    console.log('timer');
},1000);console.log(1);console.log(2);console.log(3);
登录后复制

这个没什么问题,浏览器打印的是 1 2 3 timer
但是这样呢

setTimeout(function(){
    console.log('timer');
},0);//0延时console.log(1);
console.log(2);
console.log(3);
登录后复制

浏览器打印依然打印的是 1 2 3 timer
也许有同学知道,旧版浏览器,setTimeout定时至少是10ms(即便你设置了0ms),
H5新规范是定时至少4ms(我读书少不知道为什么),改变DOM也是至少16ms
也许这是因为这个原因
那么我再改动一下代码

setTimeout(function(){
    console.log('timer');
},0);var a = +new Date();for(var i = 0; i < 1e5; i++){
    console.log(1);
}var b = +new Date();
console.log(b - a);
登录后复制

这回够刺激了吧,输出10w次,我浏览器都假死了(心疼我chrome)
不仅如此,我还打印了循环所用时间
来看看控制台

输出了10w个1,用了将近7s
timer依然是最后打印的
这就证明了我前面说的话: 同步任务总是会在异步任务之前执行
只有我执行栈空了,才会去你任务队列中取任务执行

实例

最后我举一个例子加深一下理解

demo.onclick = function(){
    console.log(&#39;click&#39;);
}function foo(a){
    var b = 1;
    bar(200);
}function bar(c){
    var d = 2;
    click//伪代码 此时触发了click事件(这里我假装程序运行到这里手动点击了demo)
    setTimeout(function(){
        console.log(&#39;timer&#39;);
    }, 0);
}
foo(100);
登录后复制

怕大家蒙我就不写Ajax了
Ajax如果处理结束后(通过Http请求线程),也会将回调函数放在任务队列中
还有一点click那一行伪代码我最开始是想用demo.click()模拟触发事件
后来在测试过程中,发现它好像跟真实触发事件不太一样
它应该是不通过触发事件线程,而是存在于执行栈中,就相当于单纯地执行click回调函数
不过这只是我自己的想法有待考证,不过这不是重点,重点是我们理解这个过程,请大家不要吐槽我╰( ̄▽ ̄)╭

下面看看执行这段代码时发生了什么(主要说栈和队列的问题,不会赘述预编译过程)

  • 主线程开始执行,产生了栈、堆、队列

  • demo节点绑定了事件click,交给事件触发线程异步监听

  • 执行foo函数(之前同样有预编译过程),创建了帧包括foo函数的形参、局部变量压入执行栈中

  • foo函数内执行bar函数,创建帧包括bar函数的形参、局部变量压入执行栈中

  • 触发了click事件,事件触发线程将回调事件处理函数放到js线程的任务队列中

  • 触发了定时器事件,事件触发线程立即(4ms)将回调处理函数放到js线程的任务队列中

  • bar函数执行完毕,弹出栈

  • foo函数执行完毕,弹出栈

  • 此时执行栈为空

  • 执行栈向任务队列中获取一个任务:click回调函数,输出‘click’

  • 执行栈项任务队列中获取一个任务:定时器回调函数,输出‘timer’

  • 执行结束

这里从任务队列里不断取任务的过程就是Event Loop

Event Loop

有一些我的理解,如果发现不对或者有疑问的地方,请联系我
相信大家看了这个例子应该对js底层运行机制有了一个大概的了解

 以上就是浏览器UI多线程及对JavaScript单线程底层运行机制的理解的内容,更多相关内容请关注PHP中文网(www.php.cn)!


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

如何在JavaScript中获取HTTP状态码的简单方法 如何在JavaScript中获取HTTP状态码的简单方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务

javascript中如何使用insertBefore javascript中如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

See all articles