JS 函数的执行时机

224 阅读2分钟

JS 执行机制

  1. 先执行执行栈中的同步任务。
  2. 遇到异步任务(回调函数)便放入任务队列中。
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

JS 的异步是通过回调函数实现的。

异步任务:

  1. 普通事件,如 click、resize 等
  2. 资源加载,如 load、error 等
  3. 定时器,包括 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);
}