异步编程到底谁先执行?

前言

我在之前的文章中写过异步(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!因为在宏任务中也可能会存在微任务,此时宏任务比微任务先执行,掘友们可千万别踩坑啊!

结尾

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

相关推荐
张拭心4 小时前
Cursor 又偷偷更新,这个功能太实用:Visual Editor for Cursor Browser
前端·人工智能
I'm Jie4 小时前
深入了解 Vue 3 组件间通信机制
前端·javascript·vue.js
用户90443816324605 小时前
90%前端都踩过的JS内存黑洞:从《你不知道的JavaScript》解锁底层逻辑与避坑指南
前端·javascript·面试
CodeCraft Studio5 小时前
文档开发组件Aspose 25.12全新发布:多模块更新,继续强化文档、图像与演示处理能力
前端·.net·ppt·aspose·文档转换·word文档开发·文档开发api
无敌最俊朗@6 小时前
STL-vector面试剖析(面试复习4)
java·面试·职场和发展
PPPPickup6 小时前
easychat项目复盘---获取联系人列表,联系人详细,删除拉黑联系人
java·前端·javascript
老前端的功夫6 小时前
前端高可靠架构:医疗级Web应用的实时通信设计与实践
前端·javascript·vue.js·ubuntu·架构·前端框架
Benmao⁢6 小时前
C语言期末复习笔记
c语言·开发语言·笔记·leetcode·面试·蓝桥杯
前端大卫6 小时前
【重磅福利】学生认证可免费领取 Gemini 3 Pro 一年
前端·人工智能
孜燃7 小时前
Flutter APP跳转Flutter APP 携带参数
前端·flutter