本章信息量较大,请慎读!!!
内容包括:(线程,主线程,进程,队列,回调函数,线程池)概念为铺垫,掌握 异步,同步
一、线程和进程基本概念
进程:操作系统分配的占有CPU资源的最小单位。拥有独立的地址空间。进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程:安排CPU执行的最小单位。同一个进程下的所有线程,共享进程的地址空间。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程,一个进程至少有一个线程。
例如:
单线程程序:即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。如,去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网。
多线程程序:即,若有多个任务可以同时执行。如,去网吧上网,网吧能够让多个人同时上网。
进程和线程算是操作系统内两个很基本、很重要的概念了,进程是操作系统中进行保护和资源分配的基本单位,操作系统分配资源以进程为基本单位。而线程是进程的组成部分,它代表了一条顺序的执行流。
二、队列的概念和作用
队列不是线程,队列是用来组织任务的,将任务加到队列中,任务会按照加入到队列中先后顺序依次执行,如果是同步队列,会在当前线程中执行,如果是异步队列,则操作系统会根据系统资源去创建新的线程去处理队列中的任务,线程的创建、维护和销毁由操作系统管理,系统会给我做很多优化
串行队列(JavaScript属于串行队列):
按照FIFO原则,顺序执行,先加入队列中的任务先执行,一个任务一任务的顺序执行,只有等到队列中上一个任务完成,才能执行下一个任务
并行队列:
任务是按照加入到队列中的顺序开始执行,但任务完成时的顺序是不确定的
队列和线程的关系:
在一个线程内可能有多个队列,这些队列可能是串行的或者是并行的,按照同步或者异步的方式工作
异步的,则会开启新的线程工作
同步的,会在当前线程内工作,不会创建新的线程
注意:并行同步队列,不会创建新的线程而且会是顺序执行相当于串行同步队列
三、主线程
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程。每个进程至少都有一个主线程,在Winform中,应该就是创建GUI的线程。
主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行比如执行各种关闭动作。
四、线程池概念
一种线程使用模式,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
首先,打开浏览器后 ,程序处于运行过程,并且具有一定独立功能。当一个程序进入内存运行,即变成一个进程。因为浏览器是一个多线程的执行环境,在浏览器的内核中分配了多个线程,最主要的线程之一即是js引擎的线程,同时js事件队列中的异步请求,交互事件触发,定时器等事件都是由浏览器的事件触发线程进行监听的,浏览器的事件触发线程被触发后会把任务加入到js 引擎的任务队列中,当js 引擎空闲时候就会开始执行该任务。
五、回调函数
字面上的理解,回调函数就是传递一个参数化的函数,就是将这个函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行传进去的作为参数的函数。走这个过程的参数化的函数 就叫做回调函数。换个说法也就是被作为参数传递到另一个函数(主函数)的那个函数就叫做 回调函数。
在Javascript编程中回调函数经常以几种方式被使用,尤其是在现代web应用开发以及库和框架中:
1.异步调用(例如读取文件,进行HTTP请求,等等)
2.时间监听器/处理器
3.setTimeout和setInterval方法
4.一般情况:精简代码
所以我们可以得出结论:
JavaScript是一门单线程语言,JavaScript是按照语句出现的顺序执行的
JavaScript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但JavaScript是单线程这一核心仍未改变。所以一切JavaScript版的”多线程”都是用单线程通过异步模拟出来的,一切JavaScript多线程都是纸老虎!
六、异步,同步
生活化理解:
同步的起床要出门:
洗澡(20分钟)-> 刷牙(5分钟)-> 喂狗(5分钟)-> 穿衣服(5分钟)-> 找钥匙(40分钟)
需要(75分钟)
同步是洗完澡之后 才会执行刷牙、喂狗等。。。
异步的起床要出门:
洗澡(20分钟)-> 刷牙(5分钟)-> 喂狗(5分钟)-> 穿衣服(5分钟)-> 找钥匙(40分钟)
需要(40分钟)
同步则是一步一步的完成 洗澡、刷牙、 喂狗等。。。
一:同步加载
同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作。所以默认同步执行才是安全的。但这样如果js中有输出document内容、修改dom、重定向等行为,就会造成页面堵塞。所以一般建议把 < script > 标签放在< body>结尾处,这样尽可能减少页面阻塞。
< script src=“http://yourdomain.com/script.js”>< /script> //同步
二:异步加载
异步加载又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有三种方式。
在html中,只有script标签中的src为同步加载,会阻塞代码,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的,可以通过defer属性和async属性,创建script标签进行解决。
defer只针对IE浏览器
async 只能加载src中的脚本,不能把代码也在script标签中 ,规定一旦脚本可用,则会异步执行
动态创建script,插入到DOM中,加载完毕后callBack
<script defer> //只针对IE浏览器
js代码...//
</script>
<!-- 第二种 async 只能加载src中的脚本,不能把代码也在script标签中 -->
<script async="async" src="03.js">
var abc = 123; //不能这样写
</script>
<script>
//异步的过程
var img = document.createElement("img");
img.src = //这个也是异步的
var script = document.createElement("script");
script.src = //这个也是异步的
document.head.appendChild(script);
</script>
在异步执行的模式下,每一个异步的任务都有其自己一个或着多个回调函数,这样当前在执行的异步任务执行完之后,不会马上执行事件队列中的下一项任务,而是执行它的回调函数,而下一项任务也不会等当前这个回调函数执行完,因为它也不能确定当前的回调合适执行完毕,只要引它被触发就会执行。
七、三种异步加载解决方式(常出没面试题)
解决方法1 : 过滤
<script>
// 异步的过程
var script = document.createElement("script");
script.src = "test.js";
document.head.appendChild(script);
setTimeout("test()",100) //等待加载完成以后执行,加载时间不确定,不推荐
</script>
解决方法2
onload 事件 --> 等待加载完成以后执行的事件
包括link标签中的 href 加载并下载完成
包括img 的src 加载并下载完成
script.onload = function(){ //老版本的IE不支持
test()
}
解决方法3
通过监听状态变化的事件来兼容IE,全面的解决方法
readyState返回当前文档的状态
uninitialized - 还未开始载入
loading - 载入中
interactive - 已加载,文档与用户可以开始交互
complete - 载入完成
onreadystatechange 当一个文档的 readyState 属性发生更改时,readystatechange 事件会被触发。
function add(url,cellback){
var script=document.createElement("script");
if (script.readyState) {
console.log(script.readyState)
script.onreadystatechange = function () {
if(script.readyState == "complete" || script.readyState == "loaded"){
cellback();
}
}
}else{
script.onload = function(){
cellback();
}
}
script.src = url;
document.head.appendChild(script)
}
add("620.ex.js",function(){
test()
})