JS 执行机制
- 先执行执行栈中的同步任务。
- 遇到异步任务(回调函数)便放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
JS 的异步是通过回调函数实现的。
异步任务:
- 普通事件,如 click、resize 等
- 资源加载,如 load、error 等
- 定时器,包括 setInterval、setTimeout 等
执行如下代码段A:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
//依次打印输出 6个6
setTimeout是异步任务,需要同步任务全部执行完才会执行。可以理解为:先干完手头的事情之后立刻做某事。
在代码段A中,是先执行完for循环,当for循环执行完后,i最后的值为6,然后执行setTimeout()函数,所以就打印输出 6个6
再执行如下代码段B:
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
//依次打印输出 0 1 2 3 4 5
在代码段B中,变量i是let声明的一个局部变量,当前的i只在本轮循环有效。而每次定时器存储的都是当前局部变量的值,分别是0,1,2,3,4,5,只是未执行打印操作,故而for循环结束后,setTimeout执行。
在 ES6 之前,还有另外的两种方法,一种是利用 setTimeout 的第三个参数,另外一种是利用闭包:
// 第一种方法
for (var i=0; i<6; i++) {
setTimeout((i)=>{
console.log(i)
}, 0, i)
}
// 第二种方法
for (var i = 0; i < 6; i++) {
!(function (j) {
setTimeout(() => {
console.log(j);
}, 0);
})(i);
}
写成箭头函数如下
// 第二种方法
for (var i = 0; i < 6; i++) {
!((j)=> {
setTimeout(() => {
console.log(j);
}, 0);
})(i);
}