异步编程到底谁先执行?

前言

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

结尾

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

相关推荐
学且思6 小时前
使用import.meta.url实现传递路径动态加载资源
前端·javascript·vue.js
problc6 小时前
OpenClaw 的前端用的React还是Vue?
前端·vue.js·react.js
冰暮流星6 小时前
javascript里面的return语句讲解
开发语言·前端·javascript
步步为营DotNet6 小时前
使用.NET 11的Native AOT提升应用性能
java·前端·.net
Never_Satisfied6 小时前
在JavaScript / HTML中,监听鼠标滚动事件
javascript·html·计算机外设
studyForMokey6 小时前
【Android面试】Context专题
android·面试·职场和发展
左耳咚6 小时前
Claude Code 记忆系统与 CLAUDE.md
前端·人工智能·claude
予你@。6 小时前
Vue 实现:点击按钮将 HTML 导出为图片(完整教程)
javascript·vue.js·html
早點睡3906 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-svg (CAPI版本)
javascript·react native·react.js
喵叔哟6 小时前
12-调用OpenAI-API
前端·人工智能·.net