异步编程到底谁先执行?

前言

我在之前的文章中写过异步(Promise:解决JavaScript异步编程难题),js是一门单线程语言,所以在程序的运行中,会有许多的异步,那么一串串代码着实让人摸不着头脑:到底谁先运行?那么本文将带领掘友们来深度剖析异步编程的运行顺序。

进程和线程

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

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

我们来说说线程

js 复制代码
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的开启)

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

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

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

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

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

那我们话不多说上代码。

js 复制代码
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

程序执行完毕!

面试官环节

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

js 复制代码
//必考题
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!因为在宏任务中也可能会存在微任务,此时宏任务比微任务先执行,掘友们可千万别踩坑啊!

结尾

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

相关推荐
唔662 分钟前
flutter实现web端实现效果
前端·flutter
csj5013 分钟前
前端基础之《React(2)—webpack简介-使用Babel》
前端·react
!win !1 小时前
分享二个实用正则
javascript·正则表达式
xw51 小时前
分享二个实用正则
javascript·正则表达式
刘新明19891 小时前
算法还原案例4-OLLVM_MD5
开发语言·前端·javascript·1024程序员节
诚实可靠王大锤1 小时前
react-native实现多列表左右滑动+滚动TabBar悬停
javascript·react native·react.js·1024程序员节
言德斐2 小时前
Python Web框架深度对比:Django vs Flask vs FastAPI(含优缺点与选型策略)
前端·python·django
疯狂的沙粒2 小时前
前端开发【工具函数】基于dayjs 封装的DateUtils工具函数,可以直接拿着使用
前端·javascript·vue.js·1024程序员节
jingling5552 小时前
CSS进阶 | 不用一行JS!用纯CSS打造会动的现代化单页应用(3D翻转卡片)
前端·javascript·css
你的电影很有趣2 小时前
lesson76:Vue.js 核心特性详解:事件处理、计算属性与侦听器
javascript·vue·1024程序员节