前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >从 JavaScript 的单线程执行说起

从 JavaScript 的单线程执行说起

作者头像
四火
发布于 2022-07-18 05:43:28
发布于 2022-07-18 05:43:28
41600
代码可运行
举报
文章被收录于专栏:四火的唠叨四火的唠叨
运行总次数:0
代码可运行

先看一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setTimeout(function(){
	alert("a");
}, 0);
while(1);
alert("b");

希望在马山可以弹出一个警告提示框 “a” 来,但是始终没有来;而且,在 FireFox 中跑还得到了这样的提示,并提示你是否要终止这段脚本的执行,遇事我选择终止以后,“a” 倒是弹出来了,但是 “b” 却弹不出来了:

Warning: Unresponsive Script A script on this page may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete.

因为浏览器多个事件放入队列中执行,每个事件执行的过程当中,是没法中断的(比如有鼠标响应事件、页面渲染事件、还有 setTimeout 定义的事件等等)。“b” 所在的那段脚本被终止了,但是 “a” 所在的那段逻辑已经进入了事件队列,并没有被终止。从这个例子也可以看出,JavaScript 的延迟执行并不准确。但是话说回来,既然这里希望马上执行,为什么要使用 setTimeout 方法呢?

原因很简单,因为这里我希望把这个弹框的逻辑放到事件队列中去。

为什么要设计成单线程的

其实 javascript 核心语言没有包含任何线程机制的,还有客户端的 javascript 也是没有明确定义线程机制,但是 javascript 还是严格按照” 单线程” 的模型去执行代码。为什么?网上很多声音都说这和它的历史有关系,但是,其实有一个更重要的原因——死锁。多线程的 GUI 框架特别容易死锁,这篇文章 《Multithreaded toolkits: A failed dream?》描述了其中的缘由,大致是说 GUI 的行为大多都是从更抽象的顶部一层一层调用到操作系统级别,而事件则是反过来,从下网上冒泡,结果就是两个方向相反的行为在碰头,给资源加锁的时候一个正序,一个逆序,极其容易出现互相等待而饿死的情况,而这种情况下要解决这一问题无异于 “fight back an oceanic tidal force”——推荐阅读。AWT 最初其实就是想设计成多线程的,但是使用者非常容易引起死锁和竞争,最后 Swing 还是做成了单线程的。但凡这种 event loop+单线程执行的模式,我们还可以找到很多,比如 JDK 的 GUI 线程模型,主线程就是一个 “主事件循环”(再后来才引入了 Event Dispatch Thread,但这并不改变整体的基本线程模型),还有 Mac 系统的 Cocoa 等等,都是这样的模式。

另外,关于 thread 还是 event,这两种典型模式的优劣比较,在 《The Case of Threads vs. Events》这篇文章中有详细的比较:

伪 sleep 方法

JavaScript 是没有 sleep 方法的,正因为它是单线程执行的,sleep 方法是没有意义的。如果非要 sleep,我们只能实现一个没有意义的伪 sleep: 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function sleep(time) {
	var start = new Date().getTime();
	while (true) {
		if (new Date().getTime() - start > time) {
			break;
		}
	}
}

但是这个伪 sleep 是没有意义的,因为这个循环会不断消耗 CPU 去比对时间(要不消耗 CPU 去比对时间是需要系统调用的,这在 JavaScript 里面是不可能实现的),并不是真正的 sleep,而是没有响应地工作。

拆分耗时逻辑

很多时候我们需要把耗时的逻辑拆分,腾出时间来给其他逻辑的执行:下面的代码源自 《Timed array processing in JavaScript》这篇文章,作者首先给出一个这样的拆分逻辑执行的框架代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function chunk(array, process, context){
    var items = array.concat();   //clone the array
    setTimeout(function(){
        var item = items.shift();
        process.call(context, item);

        if (items.length > 0){
            setTimeout(arguments.callee, 100);
        }
    }, 100);
}

但他同时也马上指出了其中的问题,100 毫秒的间隔延时太长了,也许 25 毫秒就够了,但是不能为 0,0 也可以使得这个执行拆分成多个事件进入队列,但是我们需要给 UI 的更新渲染等等留一些时间。于是他又改进了一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Copyright 2009 Nicholas C. Zakas. All rights reserved.
//MIT Licensed
function timedChunk(items, process, context, callback){
    var todo = items.concat();   //create a clone of the original

    setTimeout(function(){

        var start = +new Date();

        do {
             process.call(context, todo.shift());
        } while (todo.length > 0 && (+new Date() - start < 50));

        if (todo.length > 0){
            setTimeout(arguments.callee, 25);
        } else {
            callback(items);
        }
    }, 25);
}

可以看见,这可以更充分地利用时间,执行的任务放到一个数组中,只要每次 chunk 内执行的时间不足 50 毫秒,就继续执行;一旦超过 50 毫秒,就留给外部事件 25 毫秒去处理。

Web Worker

本质上说,web worker 是运行在后台的 JavaScript,不会影响页面的性能。 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。Web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。这可以看做是 HTML5 尝试为单线程 JavaScript 弊端做的改进(当前问题还有不少,譬如浏览器差异)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var w=new Worker("w.js");

和 web worker 通信,需要在 worker 的 w.js 中实现一个 onmessage 函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
onmessage =function (evt){
  var data = evt.data;
  postMessage(data); // 发消息给主线程
}

再在主线程中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
worker.postMessage("hello world"); //发消息给工作线程
w.onmessage = function (event) {
    // ...
}

把嵌套调用变成链式调用

这和今天的话题有点远,但是因为 setTimeout 使用得多了,这个问题几乎必现,所以在此提一提。这个问题就是嵌套层次过深的问题,这在设计 JavaScript 框架的时候尤其明显:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setTimeout(function(){
	// logic
	setTimeout(function(){
		// logic
		setTimeout(function(){
			// logic
		}, 200);
	}, 20);
}, 100);

有一个通用的优化办法,把嵌套调用优化成链式调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
runner.push(function() {
  // logic
}, 100)
.push(function() {
  // logic
}, 20)
.push(function() {
  // logic
}, 200)
.run();

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
js多线程编程
HTML5之Javascript多线程 Javascript执行机制 在HTML5之前,浏览器中JavaScript的运行都是以单线程的方式工作的,虽然有多种方式实现了对多线程的模拟(例如:Javascript 中的 setinterval 方法,setTimeout 方法等),但是在本质上程序的运行仍然是由 JavaScript 引擎以单线程调度的方式进行的。在 HTML5 中引入的工作线程使得浏览器端的 Javascript 引擎可以并发地执行 Javascript 代码,从而实现了对浏览器
前朝楚水
2018/04/02
2.3K0
JavaScript 事件循环(Event Loop)深度剖析
事件循环(Event Loop)是 JavaScript 实现异步编程的核心机制,它是为了解决 JavaScript 单线程执行模型下的非阻塞操作而设计的。事件循环负责协调和调度以下任务:
全栈若城
2025/02/20
2690
JavaScript 事件循环(Event Loop)深度剖析
从setTimeout分析浏览器线程
[toc]   今天接到阿里的面试电话,面试官很和善,聊聊天的形式不知不觉就是一个小时。本人接触前端不深,面试的时候问的几个问题也让我发现自身学习过程中思考太少,其中一个就是问到了setTimeout的工作机理,当时简单讲了讲我自己的想法,面试官也指出了其中的问题,现查阅资料重新整理记录。
csxiaoyao
2019/02/15
1.2K0
从setTimeout分析浏览器线程
JS是单线程,你了解其运行机制吗?
如果是windows电脑中,可以打开任务管理器,可以看到有一个后台进程列表。对,那里就是查看进程的地方,而且可以看到每个进程的内存资源信息以及cpu占有率。
夜尽天明
2019/11/13
2.2K0
JS是单线程,你了解其运行机制吗?
web性能优化的15条实用技巧
1.访问DOM会影响浏览器性能,修改DOM则更耗费性能,因为他会导致浏览器重新计算页面的几何变化。<通常的做法是减少访问DOM的次数,把运算尽量留在JS这一端。
徐小夕
2019/10/08
7100
JavaScript中的单线程运行,宏任务与微任务,EventLoop
上面这个问题看起来对有的同学可能很简单,到有的同学可能会比较复杂。对你不管是复杂还是简单,这其中涉及到的只是点都是一样的。JavaScript单线程,宏任务与微任务,EventLoop。这些就是这个题目的考点,理解了这些,那么上面的这道题对你来说那就是信手拈来,游刃有余。
踏浪
2019/07/31
3.5K1
JavaScript中的单线程运行,宏任务与微任务,EventLoop
这一次,彻底弄懂 JavaScript 执行机制
本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我。
前端迷
2018/12/06
1.2K0
这一次,彻底弄懂 JavaScript 执行机制
HTML5 Web Worker的使用
Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。
疯狂的技术宅
2019/03/27
6690
webWorker的详解与用法
JavaScript作为浏览器脚本语言,被定义为了只有单线程的语言,也就是同一时间只能做同一事情。如果JavaScript不是单线程,那么就有点棘手了。比如,与用户交互或者对DOM进行操作时,在一个线程上修改某个DOM,另外的线程删除DOM,这就会造成冲突。但是在HTML5中引入了webWorker的概念,为JavaScript引入了线程的概念,它允许开发人员编写能够长时间运行而不被用户所中断的后台程序,去执行事务或者逻辑,并同时保证页面对用户的响应。但是子线程完全受主线程控制,且不得修改DOM。所以一般情况下webWorker的主要用途是处理一些比较耗时的计算。
OECOM
2020/07/01
1.1K0
单线程的 JavaScript 是怎么实现异步的?
众所周知,JavaScript是单线程的,但是不可避免的,JavaScript也需要进行一些异步任务,比如下面这个例子
PHP开发工程师
2022/04/06
1.1K0
单线程的 JavaScript 是怎么实现异步的?
JavaScript多线程编程
| 导语 远离浏览器卡顿,提高用户体验,提升代码运行效率,使用多线程编程方法。 浏览器端JavaScript是以单线程的方式执行的,也就是说JavaScript和UI渲染占用同一个主线程,那就意味着,如果JavaScript进行高负载的数据处理,UI渲染就很有可能被阻断,浏览器就会出现卡顿,降低了用户体验。 为此,JavaScript提供了异步操作,比如定时器(setTimeout、setInterval)事件、Ajax请求、I/O回调等。我们可以把高负载的任务使用异步处理,它们将会被放入浏览器的事件任务
腾讯Bugly
2019/01/30
1.6K0
JavaScript多线程编程
小白理解 JavaScript 执行机制
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
前端魔法师
2021/01/27
6250
【本周主题】第一期:JavaScript单线程与异步
单线程是指,js在执行的时候,都只有一个线程来处理所有任务。这个线程就是js的主线程。
xing.org1^
2019/02/20
1.5K0
涨姿势 , JavaScript 玩转多线程编程~
浏览器端JavaScript是以单线程的方式执行的,也就是说JavaScript和UI渲染占用同一个主线程,那就意味着,如果JavaScript进行高负载的数据处理,UI渲染就很有可能被阻断,浏览器就会出现卡顿,降低了用户体验。
苏南
2020/12/16
3930
涨姿势 , JavaScript 玩转多线程编程~
JavaScript 运行机制详解:再谈Event Loop
一年前,我写了一篇《什么是 Event Loop?》,谈了我对Event Loop的理解。 上个月,我偶然看到了Philip Roberts的演讲《Help, I'm stuck in an event-loop》。这才尴尬地发现,自己的理解是错的。我决定重写这个题目,详细、完整、正确地描述JavaScript引擎的内部运行机制。下面就是我的重写。 进入正文之前,插播一条消息。我的新书《ECMAScript 6入门》出版了(版权页,内页1,内页2),铜版纸全彩印刷,非常精美,还附有索引(当然价格也比同类书籍
ruanyf
2018/04/12
1.1K0
JavaScript 运行机制详解:再谈Event Loop
Nodejs探秘:深入理解单线程实现高并发原理
为什么单线程的nodejs可以支持高并发呢?很多人都不明白其原理,自己也在很长一段时间内被这些概念搞的是云里雾里。下面我们就来一步一步揭开其神秘的面纱。
腾讯NEXT学位
2018/07/27
3.1K0
JavaScript是如何工作的:Web Workers的构建块+ 5个使用他们的场景
这次我们会逐步讲解 Web Workers,先说个简单的概念,接着讨论不同类型的 Web Workers,他们的组成部分是如何一起工作的,以及不同场景下它们各自优势和限制。最后,提供5个正确使用 Web Workers 的场景。
前端小智@大迁世界
2019/01/29
8770
JavaScript是如何工作的:Web Workers的构建块+ 5个使用他们的场景
深入理解JS异步编程四(HTML5 Web Worker)
>Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。
空空云
2018/09/27
1.1K0
JavaScript之Event Loop
先看段代码: console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve, reject) { console.log(3); resolve(); console.log(4); }).then(function () { console.log(5); }); }); function fn
laixiangran
2018/04/24
8030
JavaScript之Event Loop
Web Worker:JavaScript 中的多线程
随着 Web 应用程序变得越来越复杂和要求越来越高,对高效响应式处理的需求变得越来越重要。JavaScript 是一种单线程语言,有时可能会难以处理繁重的计算任务,这可能会导致用户界面速度慢和应用程序无响应。但是,随着 Web Worker 的引入,JavaScript 获得了利用多线程的能力,从而提高了性能并增强了用户体验。在本文中,我们将深入探讨 Web Workers 的世界,并探讨它们如何在 JavaScript 中启用多线程。
SEO-juper
2023/11/23
8280
相关推荐
js多线程编程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验