JavaScript运行机制的学习
今天在偶然在网上看到一个JavaScript的面试题,尝试着看了一下,很正常的就做错了,然后给我们前端做,哈哈,他居然也顺理成章做的错了,代码大概是这样的
/*1 下面代码会怎样执行?执行结果是什么*/
var i=true;
setTimeout('stoploops()', 2000);
loops();
function loops(){
alert('no hello world!');
while(i){ }
}
function stoploops(){
i=false;
alert('hello world!');
}
粗略的看下上面的代码,你的第一感觉是浏览器在网页打开两秒后执行stoploops函数,将i的值变成false随后弹出hello world!,然后执行loops函数弹出no hello world!,由于i的值为false所以不会进入死循环,所以代码执行结束,但代码的实际运行情况并不是这样。
经过本人查阅资料仔细一研究后,发现事情并不简单,这个问题牵连出了JavaScript代码的在浏览器中的运行机制,而setTimeout和setInterval函数也算是两个彻头彻尾的“大骗子”,而相关关于介绍setTimeout() 的文档也是:方法用于在指定的毫秒数后调用函数或计算表达式。
但是在很多情况下setTimeout和setInterval函数调用的函数并不一定会在参数指定的时间内执行,比如上面的代码;上面的代码最终运行结果是:浏览器在网页打开的第一时间会先执行loops()函数,随即弹出no hello world!弹窗,然后由于i=true,所以会无限循环,直到停电,永远都不会执行stoploops函数。
如果你是普通撸码玩家,对于这种现实也许有点无法接受,因为常规的代码都是顺序执行的,这有点违背常理的味道在里面,但是事实就是这样,代码会按照我所说的第二种方式执行。
而且弄懂为什么会这样不安常规的执行,对于深入学习和理解JS都是有很大必要的。
下面就来揭开这个问题的神秘面纱,其执行顺序错位其本质就是由于:JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序.
当然理解这句话的意思你首先得知道什么是线程:线程是程序执行流的最小单元。关于线程与进程相关细节的东西也比较复杂,一句话也说不清楚,当然在此处为了理解后面的内容,你可以简单的把线程理解为一个小型“cpu”,它在同一时间内只能做一件事情,就好比假如一个人,在同一时间内,他在吃饭的时候,就开启了一个负责吃饭的线程,这个线程就只能吃饭,不能同时做其他的事情,如果他想在吃饭的时候“边吃饭边看电视”,就要启用另一个线程,一个线程负责吃饭,一个线程负责看电视(实际的这两个线程执行是交替执行,并不是并行,所以边吃饭边看电视会有引号)。这么说我想你就能理解上面那句:JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序. 简单的说就是一个浏览器(浏览器就是一个进程)负责执行JS代码的线程只有一个,而setTimeout和setInterval使用了障眼法,制造了一种假象,好像用他俩执行的函数是单独启用了新的线程来执行的,其实并不会有新的线程被启用,从语言的设计角度来讲,JavaScript单线程运行时有它的优点的,JavaScript作为浏览器脚本语言,其主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题(想想要是真有一天,JS变成了多线程,要是把复杂线程冲突和数据同步问题都丢给JS,丢给前端,他会说我他M只是个画界面的,what fuck?Greet your family for me)。
所以JavaScript把代码任务分成了两种,一种是同步任务(synchronous)的代码,一种是异步任务(asynchronous)的代码(异步任务代码对应着一个事件队列,用于控制异步代码的执行顺序),JavaScript引擎会优先执行同步任务的代码,等所有同步任务的代码执行完成后,才会去顺序执行异步代码,如果在同步代码执行过程中出现CPU计算不过来,会消耗较长时间,那也是先等同步代码执行完成后,才会按顺序执行异步代码,我们一般常规书写的代码都是同步任务代码,而setTimeout和setInterval中执行的代码即为异步任务代码。所以现在回看上面的题目,由于loops函数是同步任务代码,JavaScript引擎会先执行loops函数,但是loops函数有一个死循环,所以JavaScript引擎线程会长期处于阻塞状态,无法执行后续的代码。。。终于真相大白!
扩展:
1,通过上面的学习,如果以后在遇到同步任务相对耗时时,我们可以将耗时的代码放到setTimeout中去执行,这样JavaScript引擎就会优先的去执行可快速执行的同步任务代码,现将可以渲染的部分先渲染出来,使用户体验更为良好
2,JavaScript引擎是单线程运行的!=整个网页都是单线程的,浏览器内核实现允许多个线程异步执行,除了javascript引擎线程外,还会有界面渲染线程,浏览器事件触发线程,相关请求线程等,
界面渲染线程:该线程负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行,该线程与JavaScript引擎线程是互斥的,所以就不难理解为什么当JavaScript引擎线程被阻塞后界面渲染线程就渲染HTML元素,导致界面一片空白了
事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
定时触发线程:浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时
3,既然JS是单线程语言,XMLHttpRequest对象实现AJAX异步请求是如何做到的?
其实请求确实是异步的,不过这请求是由浏览器新开一个线程请求,当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理,当任务被处理时,JavaScript引擎始终是单线程运行回调函数,具体点即还是单线程运行 onreadystatechange所设置的函数,但是如在执行AJAX异步请求之前同步任务中有可以阻塞JavaScript引擎线程代码,界面已久会卡死,无法进行其他操作,直到同步任务执行完成,解除阻塞,界面才可以做其他事情
参考文章:
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
http://www.iamued.com/qianduan/1645.html
JavaScript运行机制的学习的更多相关文章
- 从setTimeout谈JavaScript运行机制
从setTimeout说起 众所周知,JavaScript是单线程的编程,什么是单线程,就是说同一时间JavaScript只能执行一段代码,如果这段代码要执行很长时间,那么之后的代码只能尽情地等待它执 ...
- JavaScript 运行机制 & EventLoop
JavaScript 运行机制 & EventLoop 看阮老师博客和自己的理解,记录的学习笔记,js的单线程和 事件EventLoop 机制. 1. JavaScript是单线程 JavaS ...
- 深入理解JavaScript运行机制
深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...
- javascript运行机制
太久没更新博客了,Javascript运行机制 Record it 1.代码块 JavaScript中的代码块是指由<script>标签分割的代码段.例如: <script type ...
- JavaScript运行机制详解
JavaScript运行机制详解 var test = function(){ alert("test"); } var test2 = function(){ alert(& ...
- 深入浅出JavaScript运行机制
一.引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: console.log(1); setTimeout(function(){ console.log(3); ...
- javascript 运行机制 事件循环 浏览器缓存 (慕课网 前段跳槽面试必备 4-1,4-2,4-3)
4-1 渲染机制:-1-,什么是DOCTYPE及其作用?DTD(document type definition,文档类型定义)是一系列的语法规则,用来定义XML或(X)HTML的文件类型,浏览器会使 ...
- JavaScript运行机制与setTimeout
前段时间,老板交给了我一个任务:通过setTimeout来延后网站某些复杂资源的请求.正好借此机会,将JavaScript运行机制和setTimeout重新认真思考一遍,并将我对它们的理解整理如下. ...
- Javascript 运行机制
先看一下下面这段js代码: console.log('1'); setTimeout(function(){ console.log('2'); },0); console.log('3'); 请问打 ...
随机推荐
- sql 时期格式整理
我们经常出于某种目的需要使用各种各样的日期格式,当然我们可以使用字符串操作来构造各种日期格式,但是有现成的函数为什么不用呢? SQL Server中文版的默认的日期字段datetime格式是yyyy- ...
- js验证整数,浮点数
//验证数字(整数.浮点数都可以通过) function isfloat(oNum){ if(!oNum) return false; var strP=/^\d+(\.\d+)?$/; if(!st ...
- Mahout实战---运行第一个推荐引擎
创建输入 创建intro.csv文件,内容如下 1,101,5.0 1,102,3.0 1,103,2.5 2,101,2.0 2,102,2.5 2,103,5.0 2,104,2.0 3,101, ...
- linux下c的网络编程---转载
1.tcp协议
- CSS Sprites(CSS精灵) 的优缺点
CSS Sprites 的优点: 1.减少图片的字节 2.减少了网页的http请求,从而大大的提高了页面的性能 3.解决了网页设计师在图片命名上的困扰,只需对一张集合的图片上命 ...
- rails 过滤掉所有的html标签 strip_tags
strip_tags(html) Strips all HTML tags from the html, including comments. This usesthe html-scanner ...
- 设计模式学习--装饰者模式(Decorator Pattern)
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
- Jquery ui draggable在chrome和ie7下的bug
当页面足够长,向下滚动一些之后, 在拖动时,被拖动的div会向下产生滚动距离那么高(scrolltop)的差距 鼠标位置距div顶部差距了正好页面scroll的距离,页面scoll越多差的越多. 解决 ...
- 手把手教你写一个java的orm(完)
生成sql:select 上一篇讲了怎样生成一个sql中where的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就 ...
- Golang 使用FreeType-go进行字体
FreeType库(http://www.freetype.org/)是一个完全免费(开源)的.高质量的且可移植的字体引擎,它提供统一的接口来访问多种字体格式文件,包括TrueType, O ...