一、JavaScript 引擎是单线程的

可以从下面的代码中看到,第一个用setTimeout中的代码是死循环,由于是单线程,下面的两个定时器就没机会执行了。

setTimeout( function(){ while(true){} } , 100);

setTimeout( function(){ alert('你好!setTimeout'); } , 200);

setInterval( function(){ alert('你好!setInterval'); } , 200);

浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现3个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

JavaScript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来然后加以处理,浏览器无论再什么时候都只有一个JS线程在运行JS程序。

GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

浏览器事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。

由上图可看出,浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看作是浏览器派给它的各种任务,JavaScript引擎一直等待着任务队列中任务的到来,由于单线程关系,这些任务得进行排队,一个接着一个被引擎处理。

t1、t2....tn表示不同的时间点,tn下面对应的小方块代表该时间点的任务。

t1时刻:

1、GUI渲染线程

2、浏览器事件触发线程:

在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由图可知,对于JavaScript引擎线程来说,这事件是由其它线程异步传到任务队列尾的,由于引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理。

3、定时触发线程:

这里的浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,所以队列中的定时事件是异步事件。

4、在这t1的时间段内,继鼠标点击事件触发后,先前已设置的setTimeout定时也到达了,此刻对JavaScript引擎来说,定时触发线程产生了一个异步定时事件并放到任务队列中,该事件被排到点击事件回调之后,等待处理。同理,还是在t1时间段内,接下来某个setInterval定时器也被添加了,由于是间隔定时,在t1段内连续被触发了两次,这两个事件被排到队尾等待处理。

5、ajax异步请求:

浏览器新开一个http线程请求,当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到JavaScript引擎的处理队列中等待处理。

二、任务的执行顺序不同,显示结果也不同

1)未使用setTimeout函数

在网上找到的一段代码实例,这里用来演示一下。

do something

var doBtn = document.getElementById('doBtn')

, status = document.getElementById('status');

function sleep(ms) {

var start = new Date();

while (new Date() - start <= ms) {}

}

doBtn.onclick = function(e) {

status.innerHTML = 'doing...please wait...';

sleep(3000); // 模拟一个耗时较长的计算过程,3s

status.innerHTML = 'done';

return false;

};

我在firefox中执行了上面的代码。计划是点击“do something”按钮,然后显示“doing...please wait...”,接着执行sleep,最后显示“done”。

但是结果是点击后,浏览器卡住3秒左右,最后直接显示done。

分析下看出,在做status.innerHTML设置的时候,是需要执行GUI渲染线程的,但是现在还在执行JavaScript引擎线程,而JavaScript引擎线程与GUI渲染线程是互斥的,所以就最后显示了done。

2)使用了setTimeout函数

do something timer

var doBtn2 = document.getElementById('doBtn2')

, status2 = document.getElementById('status2');

function sleep2(ms) {

var start = new Date();

while (new Date() - start <= ms) {}

}

doBtn2.onclick = function(e) {

status2.innerHTML = 'doing...please wait...';

setTimeout(function() {

sleep2(3000);

status2.innerHTML = 'done';

}, 100);

return false;

};

在“doing...please wait...”后面加了个setTimeout,延时执行,给了浏览器渲染的时间,这个时候会显示出“doing...please wait...”的字样,然后执行sleep函数,最后显示“done”。

后面有网友发现在firefox中不起作用,的确有这个问题,后面我修改了一下代码,将局部变量的声明,onclick的绑定放到了window.onload事件中,等页面结构加载完成后,我再做脚本操作。

function sleep(ms) {

//...

}

window.onload = function() {

var doBtn = document.getElementById('doBtn'),

status = document.getElementById('status');

var doBtn2 = document.getElementById('doBtn2')

, status2 = document.getElementById('status2');

doBtn.onclick = function(e) {

//...

};

doBtn2.onclick = function(e) {

//...

};

};

以上就是本文的全部内容,希望对大家的学习有所帮助。

js等待当前线程内ajax完成,理解javascript定时器中的单线程相关推荐

  1. 2020-08-14 理解 javascript 函数中的 curry

    理解 javascript 函数中的 curry 函数柯里化: 所谓函数柯里化就是把一个函数的多个传参变成多个函数的单个传参. 函数的柯里化,是 Javascript 中函数式编程的一个重要概念.它返 ...

  2. js模板字符串自定义类名_详解JavaScript ES6中的模板字符串

    这篇文章主要介绍了详解JavaScript ES6中的模板字符串,JS的ES6版本带来诸多简洁化方面的重大改进,需要的朋友可以参考下 在 ES6 中引入了一种新的字符串字面量 - 模板字符串,除了使用 ...

  3. 深入理解JavaScript定时器

    对于浏览器内部,大部分操作都是异步的生成事件并添加到JavaScript引擎线程的队列中,然后由JavaScript引擎线程进行调度执行.因此浏览器的很多事件都是和JavaScript相结合的,但是也 ...

  4. 深入理解JavaScript定时机制

    容易欺骗别人感情的JavaScript定时器 本文地址: http://www.laruence.com/2009/09/23/1089.html 转载文章 JavaScript的setTimeout ...

  5. JavaScript可否多线程? 深入理解JavaScript定时机制

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...

  6. Java 并发编程解析 | 如何正确理解Java领域中的锁机制,我们一般需要掌握哪些理论知识?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 提起Java领域中的锁,是否有种"道不尽红尘奢恋,诉不完人间恩怨"的 ...

  7. linux进程退出所有tcp数据才发送,深入理解Node.js 进程与线程(8000长文彻底搞懂)...

    前言 进程与线程是一个程序员的必知概念,面试经常被问及,但是一些文章内容只是讲讲理论知识,可能一些小伙伴并没有真的理解,在实际开发中应用也比较少.本篇文章除了介绍概念,通过Node.js 的角度讲解进 ...

  8. java中的锁池和等待池是什么_线程的几个状态及方法,等待池和锁池的理解

    线程状态: 创建状态:创建一个线程实例 Thread thread = new Thread(); 就绪状态:在调用start()方法后,线程获取了除CPU的其他资源,处于就绪状态 执行状态:线程获取 ...

  9. auto.js停止所有线程_Java线程与并发编程实践:深入理解volatile和final变量

    同步有两种属性:互斥性和可见性.synchronized关键字与两者都有关系.Java同时也提供了一种更弱的.仅仅包含可见性的同步形式,并且只以volatile关键字关联. 假设你自己设计了一个停止线 ...

最新文章

  1. 厦门试点医疗大数据 产值有望达1200亿
  2. 隆重介绍恩智浦MCU机器学习教育套件——OpenART
  3. DbSetT().Where(e = true)之后再想Include怎么办?
  4. java基础:简单实现线程池
  5. VQEG(视频质量专家组)
  6. 二叉排序树的删除+图解
  7. CrawlSpiders
  8. 图神经网络,这到底是个什么?
  9. Android进阶--android自动化测试python+uiautomator
  10. 数字电路基础知识——锁存器与触发器的建立时间和保存时间(二)
  11. 《等一朵花开》读书感悟
  12. 《移动互联:用户体验设计指南》读书笔记4——移动UX模式
  13. 软件测试2年,想去培训性能测试自动化测试,28岁了,要不要培训?
  14. node rimraf模块 递归删除文件夹内容
  15. html5 figure和figcaption
  16. 论文复现-1论文重读:Black-Box Tuning for Language-Model-as-a-Service
  17. 计算机传真机电话,为什么计算机能发传真?语音信箱真是把语音投入到信箱里吗?...
  18. java ee字体_[分享] 23种漂亮的字体代码,
  19. (私人收藏)2019WER积木教育机器人赛(普及赛)解决方案-(全套)采集深度学习样本
  20. Linux TC 带宽管理队列规则

热门文章

  1. asp.net model 验证和取出 ErrorMessage 信息
  2. RTMP协议中文翻译(首发)(转)
  3. 03 - const static extern
  4. Spring jdbc 对象Mapper的简单封装
  5. sqlldr 导入乱码,Oracle客户端字符集问题
  6. Java学习总结 16个亮点
  7. 抓屏的各种方法(http://www.codeproject.com/KB/dialog/screencap.aspx)
  8. 麦克风设计指导与选型参考
  9. Pytorch基础(九)——损失函数
  10. mysql重命名数据表称方式_在MySQL中,使用()重命名数据表。_学小易找答案