首先我们都知道js是单线程的。
1.什么是单线程?
通俗点说,就是代码在执行过程中,另一段代码想要执行就必须等当前代码执行完成后才可以进行。我们拿一段代码来解释一下吧
for(var i=1;i<=3;i++){
setTimeout(function(){
console.log(i); //输出:4,4,4
},0)
}
我们来看一下上面的这段代码,既然延时器时间设置为0,那么应该执行一遍循环就应该立即打印出一个i,但是最终的打印结果为:4,4,4。之所以会出现上面的结果,正是因为js代码是单线程应用。在执行过程中,先遇到for循环,for循环先进入线程。当i=1时,循环走到setTimeOut后,此时的for循环还没有执行完成,setTimeOut就会被放入一个地方(线程池)等待执行。此时for循环继续执行,当i=2时,for循环仍没有执行完,这时的setTimeOut仍会被放在线程池中等待执行……依次类推,直到for循环转完3遍后,for循环执行完了,此时线程空闲了,在线程池中等待执行的setTimeOut依次执行打印i,而for循环执行完成后,i变成了4,所以打印出了三个4。
想改变上面的情况可以用以下两个方法:
//将var变为let,给setTimeOut外套一层块级作用域
for(let i=1; i<=3; i++){
setTimeOut(function(){
console.log(i); //输出的结果为1,2,3
},0);
}
//用自执行函数进行包裹
for(var i=1; i<=3; i++){
!function(i){
setTimeOut(function(){
console.log(i); //输出的结果为1,2,3
},0);
}(i)
}
2.promise和setTimeOut
JavaScript有一个执行栈。在执行代码时,遇到同步任务就放入执行栈中执行,遇到异步任务,如 Ajax setTimeout 等,就将其放入浏览器的其他模块中去处理。处理完成后将回调函数放入任务队列中,等待执行栈中的任务执行完成后就开始执行。下面以setTimeout为例。
setTimeout(function(){console.log('123')},1000)
浏览器将其放入调用栈后,检测到是异步任务,将其放入其他模块中进行处理,此时计时器开始计时,1000毫秒后将回调函数放入任务队列中,等待调用栈执行完毕后开始执行。当setTimeout和Promise放在一起的时候,由于Promise的任务队列的优先级更高,所以在处理宛调用栈中的任务后,会先处理Promise所在的任务队列。