likes
comments
collection
share

异步编程到底谁先执行?

作者站长头像
站长
· 阅读数 15

前言

进程和线程

说到js是一门单线程语言,那么什么是线程?什么又是进程

  • 进程:CPU运行指令和保存上下文所需要的时间
  • 线程:进程中的更小的单位,描述了一段指令执行所需的时间

我们来说说线程

let a = 1
console.log(a);
setTimeout(()=>{
    console.log(a);
},1000)
// let b = 2
for(let i = 0;i<100000;i++){
    console.log(i);
}

我们想想这段代码中的定时器什么时候输出。

会在 for 循环正在运行但是经过1s后执行吗?

还是等for 循环运行完之后再执行?

答案是后者。因为js是一门单线程语言,所以在他的线程中只能同时运行一个线程,如果在for 循环运行的同时输出 a,那就变成了多线程,例如Java等语言可以实现这个效果。

优点

  1. 节约内存开销
  2. 单线程没有锁的概念,节约上下文切换时间

异步的两种任务

上次我们提到了如何解决异步,这里我们再来聊聊异步的两种类型

宏任务

宏任务是指执行过程相对较长,会占用较多的时间的任务,让我们来列举一下:

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI-remdering

微任务

与宏任务相反,微任务是优先级较高的任务,一般会优先执行:

  • promise.then()
  • MutationObserver()
  • Process.nextTick()

事件循环机制(event-loop)

看到这里,我们终于来到了本文的重点!!! 我们先给大家展示优先事件循环的规则:

1.执行同步代码(这属于宏任务)

2.当调用栈为空,查询是否有异步代码需要执行

3.若有,就执行微任务

4.如果有需要,会渲染页面

5.执行宏任务(这也叫下一轮event-loop的开启)

掘友们看光看这段可能会说:这是啥呀?我也看不懂。

简而言之就是先执行同步代码,

要是遇上微任务,就存入微任务队列,遇上宏任务就存入宏任务队列

执行完同步代码,就开始依次执行微任务队列

执行完微任务队列,就开始执行宏任务队列

那我们话不多说上代码。

console.log('start');
setTimeout(()=>{
    console.log('setTimeout');
    setTimeout(()=>{
        console.log('inner');
    })
    console.log('end');
},1000)

new Promise((resolve,reject) =>{
    
    console.log('promise');
    resolve()
})
.then(()=>{
    console.log('then1');
}).then(()=>{
    console.log('then2');
})

1.代码从上往下执行,首先运行同步代码,将start打印

2.遇到setTimeout,将其存入宏任务队列中

3.promise是同步,所以要先执行,打印promise

4..then是微任务,所以将两个.then依次存入微任务队列

5.没有同步代码可以执行了,于是开始执行微任务队列,依次输出 then1 then2

6 执行宏任务,先将setTimeout打印,此时发现还有一个定时器,于是将其存入宏任务队列

7 执行同步代码打印 end

8 执行宏任务队列中的最后一个宏任务,打印inner

程序执行完毕! 异步编程到底谁先执行?

面试官环节

请你写一下以下代码的运行结果:

//必考题
console.log('script start')
async function async1() {
    await async2()//浏览器给await开小灶,await后面的代码当成同步代码立即调用
    console.log('async1 end')
}
async function async2() {
    console.log('async2 end')
}
async1()
setTimeout(function () {
    console.log('setTimeout')
}, 0)
new Promise(resolve => {
    console.log('Promise')
    resolve()
})
    .then(function () {
        console.log('promise1')
    })
    .then(function () {
        console.log('promise2')
    })
console.log('script end')

看到这里,你懵了。我的发?这是啥?这是ES7中新增的Promise的进阶版。async就代表Promise,await就是.then。

注意:

await会阻塞后续代码的执行,

将后续代码推入到微任务队列,并且await会先执行,不会进入微任务队列

接着按照上面的步骤执行就好了。

告诉大家结果:

  1. script start
  2. async2 end
  3. Promise
  4. script end
  5. async1 end
  6. promise1
  7. promise2
  8. setTimeout

微任务比宏任务先执行?

答案是NO!因为在宏任务中也可能会存在微任务,此时宏任务比微任务先执行,掘友们可千万别踩坑啊!

结尾

拿下这篇文章,就算面试官写的代码再复杂,你都能一眼识破真相!我的发,太简单了!