JS事件循环输入输出题汇总

249 阅读3分钟

总能在面试中遇到不会的输入输出,然后和面试官大眼瞪小眼(尬笑)

干脆整理一下我目前知道的几种常见的事件循环的输入输出题:

第一题:

console.log("script start")
setTimeout(()=>{
    console.log("settimeout")
},0)
let pro = new Promise(res=>{
    console.log("promise1")
    res("promise2")
})
pro.then(res=>{
    console.log(res)
})
console.log("script end")

上题结果是:

script start
promise1
script end
promise2
settimeout
  1. 先顺序执行同步代码,这里注意new Promise()内部是同步,只有传递给then/catch等是异步,因此promise1会第二步执行
  2. 接着执行微任务promise.then()的内容
  3. 最后执行宏任务settimeout的内容

第二题:

console.log("script start")
setTimeout(()=>{
    console.log("settimeout1")
},0)
let pro = new Promise(res=>{
    console.log("promise1")
    setTimeout(()=>{
        console.log("settimeout2")
    },0)
    res("promise2")
})
pro.then(res=>{
    console.log(res)
    setTimeout(()=>{
        console.log("settimeout3")
    },0)
})
console.log("script end")

上题的结果是:

script start
promise1
script end
promise2
settimeout1
settimeout2
settimeout3

原理如上不做过多解释,遵从先进先出就行。

第三题:

console.log("script start")
setTimeout(()=>{
    console.log("settimeout1")
},0)
let pro = new Promise(res=>{
    console.log("promise1")
    res("promise2")
})
let pro3 = new Promise(res=>{
    console.log("promise3")
    res("promise4")
})
pro.then(res=>{
    console.log(res)
    setTimeout(()=>{
        console.log("settimeout3")
        pro3.then(res=>{
            console.log(res)
        })
    },0)
})
console.log("script end")

上题输出是:

script start
promise1
promise3
script end
promise2
settimeout1
settimeout3
promise4

第四题:

console.log("script start")
setTimeout(()=>{
    console.log("settimeout1")
    pro3.then(res=>{
        console.log(res)
    })
},0)
let pro = new Promise(res=>{
    console.log("promise1")
    res("promise2")
})
let pro3 = new Promise(res=>{
    console.log("promise3")
    res("promise4")
})
pro.then(res=>{
    console.log(res)
    setTimeout(()=>{
        console.log("settimeout3")
    },0)
})
console.log("script end")

上题答案:

script start 
promise1
promise3
script end
promise2
settimeout1
promise4
settimeout3

这道题的关键是在执行宏任务队列中,宏任务会不会被中途出现的同步任务和微任务打断,答案是

第五题:

let pro = ()=>{
    new Promise(res=>{
        res(5)
    })
    pro().then(res1=>{
        console.log(res1)
    })
}
pro().then(res2=>{
    console.log(res2)
})

上题答案是:

栈溢出

微任务在遇到内部事件循环的时候会出现栈溢出的现象,因为微任务只有执行到任务的"}"时,才会将任务的内存释放,如果内部循环,则内存会无法释放导致溢出

现在改变下写法:

let pro = res=>{
    setTimeout(()=>{
        console.log(res)
        pro(5)
    },0)
}
pro(5)

上题的输出结果是:

一直输出5

宏任务和微任务不同,他是执行完立马会出队列,而不需要一定要走到最后的“},因此不存在栈溢出。

第六题:

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('timer1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('timer2')
  }, 0)
  console.log("async2");
}
async1();
setTimeout(() => {
  console.log('timer3')
}, 0)
console.log("start")

一次美团就遇到了类似题,但我之前只重视了promise和settimeout(哭死,直接暴毙)

async1 start
async2
start
async1 end
timer2
timer3
timer1

注意:async包裹的函数可以当作同步代码执行,直到遇到await跳出,执行函数外部的同步代码async2和外部的console.log,再跳回执行await后的同步代码,最后根据压入任务队列的顺序执行宏任务

额外注意的是:

async包裹的函数fn(),使用await接收时不一定需要fn()具有return。

如果函数定义return则返回return的输出,不然async语法会默认返回一个undefined,也就是async+await的函数一定会有返回值,其promise的状态只有fulfilled和rejected两种,不存在pending。

为什么需要注意这一点是因为,await接收的函数只有存在返回值的情况下才会执行后续的代码,不然会进行截断,代码会在await处结束。具体见下例。

第七题:

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

上题答案:

script start
async1 start
promise1
script end

如果await后跟随的是async声明的函数,则一定会继续async1 success和async1 end,但因为new Promise没有resolve,所以后续代码无法继续执行。

这里对代码进行更改:

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve()//更改代码
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

输出则会转变为:

script start
async1 start
promise1
script end
async1 success
async1 end

可以看到代码的截断消失了。