对JS函数执行时机的认识

181 阅读3分钟

JavaScript执行任务的机制

大家都知道,JavaScript是一门单线程语言。所以JavaScript执行任务只能一个一个按顺序来执行。

但是这有个问题,如果任务1执行时间过长,后边的任务必须得等着任务1执行完成后,才能够轮到它。这样必然会出现网页加载停留时间增加。

所以有了异步任务

什么是异步任务:

  • 准备要做一件事情,但不是立刻去做这件事情,而是需要等一段时间再去做。这样的话,我们不会傻傻的等待他做,而是先去忙别的事情。

在JavaScript中,最基础的异步是setTimeoutsetInterval函数。

可以去看mdn对setTimeout的解析:window.setTimeout

总结一下,它可以改变一个队列函数的执行顺序。

JavaScript函数的执行时机

函数执行的时机不同,运行结果也不同。

举个栗子

同步

栗子1:

let a = 1
function fn() {
console.log(a)
}

请问: 打印出来的值是多少?

答案: 不知道, 因为没有执行fn啊

栗子2:

let a = 1
function fn(){
  console.log(a)
}
a = 2
fn()

请问: 打印出来的值是多少?

答案: 2,

执行步骤:

  1. 声明一个变量a和一个函数fn
  2. 将2赋值给a
  3. 调用fn函数

栗子3:

let a = 1
function fn(){
  console.log(a)
}
fn()
a = 2

请问: 打印出来的值是多少?

答案: 1,

执行步骤:

  1. 声明一个变量a和一个函数fn
  2. 调用fn函数
  3. 将2赋值给a

上面3个例子就体现了同步任务执行步骤,就是一步一步的按顺序执行。

异步

再来举栗子!

let a = 1
function fn(){
  setTimeout(()=>{
    console.log(a)
  },0)
}
fn() 
a = 2

请问:打印出来的a是多少?

答案: 2,

执行步骤:

  1. 声明变量a和函数fn
  2. 把 1 赋值给 a,
  3. 执行fn()
    1. 因为有 setTimeout 所以要等到同步任务执行完后,再去执行
  4. 把 2 赋值给 a
  5. 执行 setTimeout() 打印出 a的值:2,

经典面试题

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

请问: 打印出的 i 是多少?

很简单啊,不是0、1、2、3、4、5吗?

错啦! 正确答案: 打印出 6个6

解释: 因为 setTimeout 是异步的, 需要等会同步的执行完再执行, 所以当for循环执行完毕后,i的值是6,然后再执行setTimeout,当然是执行6次哦,所以答案是 6个6.

怎么才能输出0、1、2、3、4、5呢?

有很多方法:

方法1: 把let写在for循环里面,这样可以单纯为每个i创造一个块级作用域,也就是复制6个i。

for(let i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

方法2: 利用立即执行函数

let i = 0
for(i = 0; i<6; i++){
 !function(i){  
   setTimeout(()=>{
      console.log(i)
    },0)
  }(i)
}

方法3: 利用 setTimeout 的第三个参数

let i = 0
for(i = 0; i<6; i++){
   setTimeout((i)=>{
      console.log(i)
    },0,i)
}