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'); 请问打 ...
随机推荐
- 使用python遍历文件夹取出特定的字符串
# -*- coding: utf-8 -* import re import os # 需要处理的文件夹路径(绝对路径) path = u"/Users/a140/Downloads/te ...
- Struts全局跳转
1.在struts配置文件中配置一个全局跳转 2.然后关联一个jsp文件 4.关联好以后会出现如图所示页面 在代码中会有如图所示的代码增加 5.然后再action中设置跳转
- mysql查看权限的命令
mysql查看用户权限的命令 1.这里用来查看用户存储过程: show grants for 用户; eg: show grants for root@'localhost';#这样就会把root用户 ...
- Nodejs学习笔记(十三)—PM2
简介 PM2 pm2是一个内置负载均衡的node.js应用进程管理器(也支持Windows),其它的类似功能也有不少,但是感觉pm2功能更强,更值的推荐 GitHub地址:https://github ...
- java内存中的对象
前记:几天前,在浏览网页时偶然的发现一道以前就看过很多遍的面试题,题目是:“请说出‘equals’和‘==’的区别”,当时我觉得我还是挺懂的,在心里答了一点(比如我们都知道的:‘==’比较两个引用是否 ...
- 牛客网剑指offer java 全部题解
经过数月的努力,终于更完了牛客网的66道剑指offer,以下的顺序和大家在牛客网的顺序是一样的(排序也花了不少时间),希望对大家找工作/提高算法能力能起到些许帮助. 每天一道剑指offer-二维数组中 ...
- Golang 知识图谱
- Ionic3与Angular4新特性
之前(17年3月底)Angular4.0.0正式发布,这个月(4月十几号)Ionic3又发布了,很多人看到这个估计都是一脸懵圈,其实,Angular4只是Angular2的后续版本,Ionic3也是I ...
- c#基础学习(0709)之vs通过抽象方法实现多态
抽象类不能用来实例化对象 1.抽象类中可以有实例成员,也可以有抽象成员 2.抽象成员不能有任何实现 3.抽象类.抽象成员必须包含在抽象类中 4.抽象类不能用来实例化对象,既然抽象类不能被实例化,那么抽 ...
- Node.js Express 框架2
文件上传 以下我们创建一个用于上传文件的表单,使用 POST 方法,表单 enctype 属性设置为 multipart/form-data. index.html <html> < ...