彻底弄懂 JavaScript 异步任务处理原理

news2024/11/16 13:43:48

目录

1.单线程

什么是单线程?

2.同步和异步

同步

异步

3.事件循环(EventLoop)

1.事件循环的基本概念

2.微任务/宏任务

3.宏任务和微任务的执行顺序

4.常见的面试题


1.单线程

首先我们需要明白JS是单线程的,这是为了降低程序复杂性,但同时为了多个事件能同时被处理,JS提供了异步的处理方式(其实JS本身是没有异步这一说法的,都是由执行环境所提供的)

什么是单线程?

就行马路的单向通行道一样,一排车只能一辆一辆的排队前行通过。

2.同步和异步

程序同一时间只做一件事! 场景如下:

同步

同步阻塞的概念指的是A调用B,B执行完获得结果返回给A,A在等待B执行过程中是停滞的状态,直到拿到结果才会往下执行

异步

异步与同步一样,其实也是一种消息通知机制,引用上面的比方,A调用B,无需等待B的结果,A继续往下执行,的同时B也在执行,B通过状态、通知等方式来通知A或者走回调来处理结果,做一件事的时候不用等待事情的结果,继续往下走,等有了结果再来通知我

3.事件循环(EventLoop)

1.事件循环的基本概念

💡 Tips:在js执行同步任务和异步任务的过程中,在主线程执行栈中的同步任务执行完毕就会,读取任务队列中的回调函数,将回调函数放到主线程执行栈中执行,执行完毕又会继续读取任务队列中的回调函数,放到主线程执行栈中执行,如此往复的过程称为事件循环(EventLoop);

 

2.微任务/宏任务

💡 Tips:以上内容已经讲解了同步任务和异步任务的概念,javascript将异步任务又进一步划分成了宏任务和微任务;

常见的宏任务有:Ajax请求、文件操作、setTimeout、setInterval、Dom操作、<Script>内容</Script>、

常见的微任务: Promise的then、catch、finally方法,Process.nextTick,mutationObserver;

 

3.宏任务和微任务的执行顺序

在每一个宏任务执行完毕后就会去查找是否存在微任务,如果微任务存在就将微任务执行完毕再去执行下一个宏任务,以此往复,直到所有的任务执行结束

4.常见的面试题

经典面试题
async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

⛳️ 分析过程

1.定义一个异步函数 async1

2.定义一个异步函数 async2

3.打印 ‘script start’ // *1

4.定义一个定时器(宏任务,优先级低于微任务),在0ms 之后输出

5.执行异步函数 async1

  • 打印 'async1 start' // *2
  • 遇到await 表达式,执行 await 后面的 async2 //关键的地方,await后面的代码会阻塞 ,async2执行完毕后,后面的代码类似于传入then()中的回调
  • 打印 'async2' // *3
  • 返回一个 Promise,跳出 async1 函数体

6.执行 new Promise 里的语句

  • 打印 ‘promise1‘ // *
  • resolve() , 返回一个 Promise 对象,把这个 Promise 压进队列里

7.打印 ’script end’ // *

8.同步栈执行完毕

9.回到 async1 的函数体,async2 函数没有返回 Promise,所以把要等async2 的值 resolve,把 Promise 压进队列

10.执行 new Promise 后面的 .then,打印 ’promise2‘ // *6

11.回到 async1 的函数体,await 返回 Promise.resolve() ,然后打印后面的 ’async1 end‘ // *7

12.最后执行定时器(宏任务) setTimeout,打印 ’setTimeout‘ // *8

⛳️ 答案:

  • script start
  • async1 start
  • async2
  • promise1
  • script end
  • promise2
  • async1 end
  • setTimeout

async2改造一下

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
function async2(){ // 去掉了 async 关键字
    console.log('async2');
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

⛳️ 后续结论

  • script start
  • async1 start
  • async2
  • promise1
  • script end
  • async1 end
  • promise2
  • setTimeout

 

console.log("script start")
    new Promise((resolve) => {
            console.log("promise1")
            resolve('微任务1')
        })
        .then((data) => { //第一个Promise加入微任务列表
            console.log(data)
        })
    async function fun1() {
        console.log("fun1")
        let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调
        console.log(data)
        console.log("await堵塞")
    }
 
    setTimeout(() => {
        console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列
    }, 0)
    async function fun2() {
        console.log("fun2")
        // return "no promise"
        return new Promise((resolve, reject) => {
            resolve("fun2 微任务")
        })
 
        // return "await"
 
    }
    fun1() //代码开始执行
    new Promise((resolve) => {
            console.log("promise2")
            resolve("微任务2")
        })
        .then((data) => { //promise状态已经fulfilled,直接加入微任务队列
            console.log(data)
        })
 
 
    console.log("同步end") // 同步代码结束

⛳️ 上面代码,执行结果(这种情况是: await后面的异步函数return的是一个Promise)

  1. script start
  2. promise1
  3. fun1
  4. fun2
  5. promise2
  6. 同步end
  7. 微任务1
  8. 微任务2
  9. fun2 微任务
  10. await堵塞
  11. setTimeout 宏任务
console.log("script start")
    new Promise((resolve) => {
            console.log("promise1")
            resolve('微任务1')
        })
        .then((data) => { //第一个Promise加入微任务列表
            console.log(data)
        })
    async function fun1() {
        console.log("fun1")
        let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调
        console.log(data)
        console.log("await堵塞")
    }
 
    setTimeout(() => {
        console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列
    }, 0)
    async function fun2() {
        console.log("fun2")
        return "no promise"
        // return new Promise((resolve, reject) => {
        //     resolve("fun2 微任务")
        // })
 
        // return "await"
 
    }
    fun1() //代码开始执行
    new Promise((resolve) => {
            console.log("promise2")
            resolve("微任务2")
        })
        .then((data) => { //promise状态已经fulfilled,直接加入微任务队列
            console.log(data)
        })
 
 
    console.log("同步end") // 同步代码结束

 ⛳️ 上面代码,执行结果(这种情况是: await后面的异步函数return的不是Promise)

  1. script start
  2. promise1
  3. fun1
  4. fun2
  5. promise2
  6. 同步end
  7. 微任务1
  8. no promise
  9. await堵塞
  10. 微任务2
  11. setTimeout 宏任务

 

⚠️ 需要注意的点

  • 从结果上来看,await后面的异步函数return的是否是Promise,执行顺序的不同点在于await后面的代码执行是在微任务2之前还是之后执行,也就是微任务队列顺序的问题
  • 当return的是Promise时:await后面的代码会在微任务2之后执行
  • 当return的不是Promise时:await后面的代码会在微任务2之前执行

⚠️ 当return的不是promise时

  • async声明的异步函数会自动将返回包装成promise ,到调用fun2这个异步函数时,await就可以看做是使用then方法,会将回调函数(这里就是await后面的代码) 放入微任务队列,这里是排在微任务一之后,微任务二之前,所以这里await后面的代码会在微任务2之前执行

⚠️ 当return的是promise时

  • 当resolve的参数是一个promise时,此时会将这个参数promise放入微任务队列(此时排在微任务1之后),而他的父级promise状态不会改变,等到微任务队列中执行到这个promise时,父promise的then方法绑定到子promise上,所以这就解释了上面的执行顺序的问题

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/642504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows上安装绿色版mysql-8.0.26

以8.0.26为例 1.安装包下载 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/下载后文件如图所示&#xff1a; 解压压缩包&#xff0c;并修改文件夹名称为mysql&#xff1a; 2.创建配置文件 在E:\mysql&#xff08;MySQL解压目录&#xff09;文件夹下&#…

VS Code 实用快捷键

在使用VS Code 开发过程中&#xff0c;熟练使用一些快捷键可以极大的便利我们的开发。所以&#xff0c;本文汇总了一些快捷键&#xff0c;以便想使用时忘记了可以快速查找到。 1. Alt 鼠标点击 在每一个点击的地方添加输入光标 2. Alt Shift 鼠标左键按住拖动 竖列多行选择。…

达梦数据库无法打开图形化界面的解决方法

在安装或使用DM数据库中&#xff0c;我们经常会需要使用图形化界面&#xff0c;但某些情况下&#xff0c;我们可能会打开图形化界面失败。本文分别介绍通过VNC或者VMware连接数据库服务器&#xff0c;和使用Xmanager第三方工具连接服务器打开图形化界面的方法。实验环境的操作系…

【ARM AMBA AXI 入门 6 - AXI3 协议中的锁定访问之AxLOCK信号】

文章目录 1.1 Locked accesses 1.1 Locked accesses 当主机使用 AxLOCK 信号来指示事务是锁定的事务时&#xff0c;互连(Interconnect)必须确保只有该主机可以访问目标从属区域&#xff0c;直到来自同一主机的未锁定事务完成。互连中的仲裁器(arbiter)必须执行此限制。 在主机…

【面试题】前端面试 15 问高频题

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 数组去重 遍历旧数组&#xff0c;然后拿着旧数组元素去查询新数组&#xff0c;如果该元素…

Linux安装mysql8

Linux安装mysql8 第一步&#xff1a;解压mysql安装包&#xff1a;tar -xvf mysql-server-8.0.25.tar.gz 第二步&#xff1a;更新文件夹名&#xff1a;mv mysql-server-8.0.25 mysql 第三步&#xff1a;进入mysql文件夹&#xff1a;cd mysql&#xff0c;创建data文件夹&#x…

OM6621PW蓝牙智能指纹锁(附芯片选型)

随着科技的快速发展&#xff0c;智能家居正逐渐成为现代生活的一部分。在智能家居领域&#xff0c;安全始终是人们关注的焦点。传统的机械锁存在被暴力破解、易被复制钥匙等安全隐患&#xff0c;无法满足当今社会对安全性的需求。因此&#xff0c;越来越多的家庭和商业场所开始…

【vue3】12-Vue 3中的Composition Api(二)

Vue3 - composition Api setup中的函数&#xff08;1&#xff09;computed函数使用&#xff08;2&#xff09;setup获取元素或组件&#xff08;3&#xff09;组件的生命周期函数&#xff08;4&#xff09;provide/inject函数&#xff08;了解&#xff09;&#xff08;5&#xf…

Java实现PPT转MP4文件

环境要求 必须是Windows电脑&#xff0c;已安装微软Office且已激活。 引入Jar 首先在pom中引入jar包 <dependency><groupId>cc.pptshow</groupId><artifactId>pptshow</artifactId><version>1.3</version> </dependency> …

【2023年计划大纲】2023年技术笔记大纲

2023年技术笔记写作计划 按照技术类型&#xff0c;计划写以下三个领域的笔记&#xff1a; &#xff08;1&#xff09;AUTOSAR标准体系的专业知识&#xff0c;T-BOX,BMS,VCU这几个产品的设计和核心开发。包括UDS协议&#xff0c;XCP协议&#xff0c;OS操作系统。 每一个产品和…

测试工程师常见的面试问题及回答建议

说起软件测试近几年的发展&#xff0c;其实已悄无声息地发生了巨大的变化。前几年随着互联网行业的迅猛发展&#xff0c;软件测试人才稀缺&#xff0c;低门槛高薪资促使大批毕业生和转行人员一窝蜂地涌入。而现在&#xff0c;软件测试发展太快&#xff0c;纵观各大招聘网站&…

Prompt是什么_揭秘Prompt:大模型时代的关键技术与实践应用

文章目录 1 Prompt是什么Prompt的定义和基本概念Prompt的工作原理 2 综合案例案例1&#xff1a;根据内容提取数据生成表格百度&#xff1a;文心一言科大讯飞&#xff1a;讯飞星火 案例2&#xff1a;文生图百度&#xff1a;文心一言科大讯飞&#xff1a;讯飞星火 案例3&#xff…

如何建立一个好的待办事项系统并提高工作效率

如果你有一系列的任务需要完成&#xff0c;你会如何记住它们呢&#xff1f; 最简单的方法当然是将它们记在脑中&#xff0c;但如果任务的数量很多&#xff0c;记忆的细节难免模糊。纸和笔也是一个不错的选择&#xff0c;但纸质的任务清单容易遗失或者损坏。让应用来帮忙当然也…

ubuntu iptables开机自启动

一、配置ubuntu路由转发 用在一台电脑有多个网卡的情形下&#xff0c;一个网卡5网段、一个网卡8网段&#xff0c;8网段是网络出口&#xff0c;所以5网段的设备需要入网的话&#xff0c;要路由转发。 sudo iptables -t nat -A POSTROUTING -s 192.168.5.0/24 -j SNAT --to-sou…

对于中小企业,如何管理好仓库?

对于中小企业&#xff0c;如何管理好仓库&#xff1f; 在制造业摸爬滚打的这些年&#xff0c;也拜访了不少客户&#xff0c;帮助不少企业做了数字化方案&#xff0c;正如题主所说&#xff0c;“仓库是每一个经销商的根据地&#xff0c;如果不把仓库管理好&#xff0c;那么整个…

产品经理认证NPDP考试心得

什么是NPDP&#xff1f; 产品经理国际资格认证NPDP&#xff08;New Product Development Professional&#xff09;&#xff0c;由美国产品开发与管理协会&#xff08;PDMA&#xff09;所发起&#xff0c;是国际公认的新产品开发专业认证。 NPDP考试简介 考试方式&#xff1a…

C语言实现顺序表与链表创建

线性表 用于存储若干相同属性元素的有序序列称为线性表。 线性表特征&#xff1a; 存在唯一的第一个元素&#xff1b;存在唯一的最后一个元素&#xff1b;除第一个序列的每一个元素元素都有一个前驱元素&#xff0c;后一个都有一个后继元素。 顺序表 线性表的顺序表示指的…

智慧矿山成行业新趋势,千寻位置助力企业数字化转型

随着政策推动和科技发展&#xff0c;智慧矿山已成为矿业行业的趋势和未来的方向。 智慧矿山就是以矿山数字化、信息化为前提和基础&#xff0c;对矿山生产、人员健康与安全、技术支持与后勤保障等进行主动感知、自动分析、快速处理&#xff0c;最终实现安全矿山、无人矿山、高效…

java设计模式之:访问者模式

前言 关于设计模式&#xff0c;我们得结合生活中的案例来学习&#xff1b;最近我在网上也看了不少文章&#xff0c;今天想跟大家分享一下关于访问者模式的一些知识&#xff0c;先来看一个简单的案例吧。 相信大家都去过医院&#xff0c;看完病&#xff0c;医生都会给我们开一…

Linux重定向和缓冲区理解

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 重定向和缓冲区理解 前言正文文件描述符重定向重定向原理重定向命令重定向函数 缓冲区缓冲区是什么&#xff1f;缓冲区刷新策略内核缓冲区与普通缓冲区 最后 前言 前…