主要知识点:异步任务和同步任务
知识铺垫:
浏览器有五个线程:
Ⅰ:GUI渲染线程
Ⅱ:JS引擎线程
Ⅲ:定时触发线程
Ⅳ:浏览器事件处理线程
Ⅴ:http请求线程
除去2以外,其他的都不属于JS,而是与JS同属于浏览器,他们与JS互相配合。
首先JS引擎是单线程执行的:即一次只做一件事
举个栗子:
function dieloop(){
while(true){ //这是一个死循环的函数
console.log("i")
}
}
dieloop();
console.log("I am a little weak")
//在死循环执行时,"I am a little weak"这句话永远不能被打印出来
//因为JS一次只处理一个任务,上一个任务没有处理完,下一个任务永远不会执行
GUI渲染线程与JS引擎线程是互斥的
GUI线程做的事情就是:凡是用户看得见的效果就是GUI线程去做的
再举个栗子
<button></button>
//点击按钮的时候有一个按钮的专属特效,这个是GUI渲染线程渲染的
//但是JS引擎线程在执行dieloop这个死循环时,你再去点击按钮,那个特效就没有了
//说明在JS引擎线程运作的时候,GUI线程是冻结的
function dieloop(){
while(true){
console.log("i")
}
}
dieloop();
浏览器处理线程:它会监控click,mouse等等交互事件,当交互发生后,就会将对应的事件处理函数放入JS引擎线程里面,再有JS引擎线程去执行。
进入正题
JS是单线程运行,但是又可异步执行
当**任务是一个简单的函数时***(不涉及事件触发/网络请求/定时器)时,它就会走左边那条线,进入同步任务,被放入主线程之中***,被挨个执行。
当**任务涉及事件触发/网络请求/定时器时,就会走右边那条线,进入Event Table,当事件被触发/数据被请求回来了/定时器时间到了,就会注册一个相对应的回调函数,再进入Event Queue,当主线程里面的任务被执行完毕后,再将Event Queue里面的任务,放入主线程里面,挨个执行。
当主线程里面的任务执行完毕后,它会去任务队列里面不断看有没有新的函数,如果有的话,马上执行,这个过程叫做事件循环
**同步任务讲解**
function foo(out) {
function bar(inn) {
console.log(inn);
}
bar("inner");
console.log(out);
}
foo("outer")
inner
outer
步骤:
Ⅰ.当代码没有执行时,执行栈为空。
Ⅱ:当foo执行时,创建了一帧(里面包含,形参、局部变量(预编译结果)
Ⅲ:在执行foo的内部,执行了bar
Ⅳ:创建了新的一帧,压入栈中
记住栈的特点是吃了吐,先进去的后出来,后进去的先出来
Ⅴ:bar后进去,所以先执行完,弹出栈
Ⅵ:foo再执行,执行完毕弹出栈
Ⅶ:栈为空
执行栈就等于主线程,主线程只有一条,是单线程执行运动
**异步任务讲解**
举个栗子
$.ajax(function(){
url:××××,
data:{},
success:function(data){
console.log(data);
}
})
console.log("run");
//执行结果:
//run
//data数据
步骤:
Ⅰajax进入Event Table,注册回调函数success,这个时候就等待数据回来
Ⅱ:console.log(“run”),也是一个任务,且是一个同步任务,它进入主线程
开始执行。
Ⅲ:ajax将数据拿回来之后,http请求线程将任务放入Event Queue之中,
主线程任务执行完毕后,开始执行success函数。
**重新理解定时器**
setTimeout并不是等待时间到了之后,就直接开始执行后面的函数,而是等时间到了,将其推入浏览器线程的任务队列里面,在主线程里面的任务执行完毕后,才开始按个调用任务队列里面的函数。
function long(){
我执行了一分钟
}
setTimeout(function(){
},100)
定时器的代码内容并不会在100ms之后就开始执行,它要等到上面long函数,再执行,所以时间是肯定大于100ms的。
即使任务队列里面没有任务,且等待时间为0ms,定时器也不可能是立刻执行的。
setInterval(function(){
},0)
也是至少4ms之后才执行,因为将定时器放入浏览器队列,再放入主线程这个过程,都需要4ms。
打出结论:执行任务的主线程是单线程,配合工作的其他线程是异步的