重学JavaScript高级(十二):async/await-事件循环-面试高频

async/await-事件循环

前面我们学习了生成器和迭代器,那么在本篇文章中,我们主要讲解生成器与Promise的结合使用,从而引出async/await语法,同时会涉及面试中频次最高的一个知识点:事件循环

生成器与异步处理

  • 首先需要了解回调地狱
    • 在Promise出来之前,我们多次请求网络接口,有可能产生回调地狱
//伪代码
function request(url) {//请求的逻辑代码//返回一个结果return res;
}
//这样一层嵌套着一层,就是回调地狱
request("第一次").then((res1) => {request("第二次" + res1).then((res2) => {request("第三次" + res2);});
});
  • 而Promise的出现就解决了回调地狱的问题
    • 不明白return的返回值的,可以看我先前的文章,Promise–详解
function request(url) {return new Promise((resolve, reject) => {resolve(url);});
}//这样就解决了回调地狱的问题,将代码写成了链式调用
request("第一次").then((res1) => {console.log(res1);return request("第二次" + res1);}).then((res2) => {console.log(res2);return request("第三次" + res2);}).then((res3) => {console.log(res3);return request("结束");});
  • 以上代码虽然解决了回调地狱的问题,但是看上去不是很优雅
  • 因此可以和生成器相结合,优化出以下的代码
/*理想代码
function getData(){let res = yield request(url);let res1 = yield request(res);let res2 = request(res1);
}
*/
function request(url) {return new Promise((resolve, reject) => {resolve(url);});
}//在获取上一次结果之后,再进行下一次请求
//因此我们就可以应用到yield
function* getdata(url) {//yield空格后面的函数会直接执行let res = yield request(url);let res1 = yield request(res);let res2 = yield request(res1);console.log("请求结束");
}//执行生成器函数
let generator = getdata("test");
//这时候执行next,其返回值{value:Promise,done{false}}
//因此我们获取value调用then方法,就可以获取resolve的值
// console.log(generator.next());generator.next().value.then((res) => {console.log(res);//第一次执行完之后,就会去进行下一次请求generator.next(res + "test").value.then((res1) => {console.log(res1);//第二次执行完之后,就会进行第三次generator.next(res1 + "test").value.then((res2) => {console.log(res2);//此时运行最后一行console代码generator.next();});});
});
  • 而我们只希望有 getdata这种函数,所以引出了 async/await的语法
  • 实际上是Promise与生成器的语法糖
function request(url) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(url);}, 2000);});
}//在获取上一次结果之后,再进行下一次请求
//因此我们就可以应用到yield
async function getdata(url) {//yield空格后面的函数会直接执行let res = await request(url + 1);console.log(res);let res1 = await request(res + 2);console.log(res1);let res2 = await request(res1 + 3);console.log(res2);console.log("请求结束");
}
getdata("test");

异步函数async/await

异步函数结构

函数分为:普通函数、生成器函数、异步函数,本次就开始学习异步函数

  • 在函数前面加 async即可,并当成普通函数执行
async function foo(){}const bar = async ()=>{}

异步函数的返回值

异步函数内部代码执行过程和普通函数是一致的

  • 普通函数没有指定返回值的时候,返回的是undefined;而异步函数返回值相当于被Promise.resolve()包裹
  • 如果返回的是一个Promise,则会由这个Promise决定
  • 如果返回的是一个对象,且对象中有then方法,则会由这个then方法决定
//其他情况之前的文章有讲过,不再演示
async function foo() {return 123;
}foo().then((res) => {console.log(res);//123
});

异步函数的异常

异步函数返回的是一个Promise,Promise有三种状态,正常执行是pending,return是fulfilled,那么何时抛出异常

  • 一些api使用错误时
  • 主动通过throw抛出
async function foo() {//在执行的时候,不会立即报错,会将错误信息用reject包裹"123".filter();//或者使用throw进行抛出异常throw new Error("主动抛出异常")return 123;
}foo().then((res) => {console.log(res); //123}).catch((err) => {console.log(err);//不会影响下面的代码执行console.log(123456);});

await关键字的使用

  • 必须在异步函数中使用
  • 一般后面跟着一个表达式,且这个表达式返回的是一个Promise(await后面可以直接跟一个普通函数或者数据,但是没有任何意义)
  • await会等到 Promise状态为fulfilled的时候,继续执行下面的代码
  • 若Promise返回的是rejected,就需要进行捕获
function request(count) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(count);}, 1000);});
}async function getData() {let res = await request(1);console.log(res);let res1 = await request(res + 1);console.log(res1);let res2 = await request(res1 + 1);console.log(res2);
}getData();
  • 捕获异常的方式
function request(count) {return new Promise((resolve, reject) => {setTimeout(() => {reject(count);}, 1000);});
}
//方式一
async function getData() {let res = await request(1);console.log(res);let res1 = await request(res + 1);console.log(res1);let res2 = await request(res1 + 1);console.log(res2);
}
//因为异步函数的返回值是Promise
getData().catch((err)=>{console.log(err)
})//方式二,使用try catch
async function getData() {try {let res = await request(1);console.log(res);let res1 = await request(res + 1);console.log(res1);let res2 = await request(res1 + 1);console.log(res2);} catch (error) {console.log(error);}
}

await/async关键字的结合使用

  • 多个异步函数之间可以使用await
async function first(){}
async function second(){}
async function third(){}async function foo(){//第一个函数执行完成,且返回的是fulfilled状态,才会执行下面的代码await first()await second()await third()
}foo()

进程和线程

  • 进程和线程是操作系统的两个概念
    • 进程:计算机已经运行的程序(打开应用程序,就会开启至少一个进程),是操作系统管理程序的一种方式
    • 线程:操作系统能够运行 运算的调度的最小单位,通常情况下它被包含在进程中
  • 以下是形象的解释
    • 进程:可以认为,打开一个应用程序,就会默认启动一个进程(可以是多进程)
    • 线程,每一个进程中,都会至少启动一个线程来执行其中的代码(也有可能是多线程)
    • 所以可以说,进程是线程的容器

浏览器中的JS线程

  • JS是单线程(可以开启workers),但是JS的线程有自己的 容器:浏览器和Node

  • 浏览器是多进程的,当我们打开 一个新的tab页面的时候,就会开启一个新的进程,这是为了防止一个页面卡死,而造成所有页面无法响应

    • 每个进程会有多个线程,其中有一个线程是专门分给JS代码运行的

    • 即,JS代码一次只能做一次事情,如果这个线程十分耗时,当前线程 就会阻塞

  • 所以真正耗时的操作,并不是由JS线程在执行的

    • 浏览器每个进程都是多线程的,那么其他线程可以来完成这个耗时的操作
    • 比如网络请求、定时器,我们只需要在特定的时候执行应该有的回调即可

浏览器的事件循环

  • 首先了解一下事件队列
    • 浏览器在处理DOM监听,ajax请求,定时器的时候,会将相关函数里面的回调函数,放入事件队列中
    • 事件队列是一种数据结构,遵循先进先出的原则
  • 那么在JS执行的过程中,遇见了定时器等相关事件,又会怎么操作
    • 首先会执行函数,但是不会执行里面的回调函数
    • 将里面的回调函数交由浏览器其他线程
    • 其他线程在处理好之后,将回调函数放入事件队列中
    • 待执行栈中空了,就会从事件队列中取出待执行的函数

image.png

微任务和宏任务

事件循环中并非只维护者一个队列,事实上有两个队列:宏任务队列和微任务队列

  • 宏任务队列:ajax、setTimeout、setInterval、DOM监听、Rendering等

  • **微任务队列:**Promise的then回调(Promise中的代码会立即执行),Mutation Observer API queueMicrotask()等

  • 那么这两个队列的优先级是什么样的

    • main script中的代码优先执行
    • 再执行 任何一个宏任务之前(不是队列,是单个的宏任务),都会先去检查微任务队列中,是否有任务需要执行
      • 也就是说 每一个宏任务执行之前,必须保证微任务的队列是空的
      • 如果不为空,那么就优先执行微任务队列中的任务
      • 若微任务中的代码不断往微任务队列中增加任务,则宏任务中的代码则会无线延期

抛出异常-throw

遇到throw,代码就会停止执行,且后面的代码不会执行

  • throw后面可以跟String、number、boolean、对象
throw {message:"错误",code:-1001}
  • 后面可以接表达式(new Error)
throw new Error("这是错误")
//会把错误地方的调用栈,错误信息都会打印出来

捕获异常的方式

当代码抛出异常之后,不去捕获的话,就会很危险

try catch捕获异常

  • 如果我们在调用一个函数的时候,这个函数抛出了异常,但是并没有对 这个异常进行处理,那么这个 异常会继续传递到上一个函数调用中(会一层一层向上传递)
  • 如果到了全局,依旧没有对异常进行处理,浏览器就会终止程序
function bar(){throw "我是错误"
}
function foo(){try{foo()}catcha(error){console.log(error)}
}
foo()

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

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

相关文章

【Chrono Engine学习总结】4-vehicle-4.1-vehicle的基本概念

由于Chrono的官方教程在一些细节方面解释的并不清楚,自己做了一些尝试,做学习总结。 1、基本介绍 Vehicle Overview Vehicle Mannel Vehicle的官方demo 1.1 Vehicle的构型 一个车辆由许多子系统构成:悬挂、转向、轮子/履带、刹车/油门、动…

搜索专项---最短路模型

文章目录 迷宫问题武士风度的牛抓住那头牛 一、迷宫问题OJ链接 本题思路:只需要记录各个点是有哪个点走过来的,就能递推得出路径。记录前驱假设从 1,1 这个点向下走到了2, 1,则将2,1这个点的前驱记为1,1。这样,将整张地图 bfs 后&#xff0c…

C++进阶(十五)C++的类型转换

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、C语言中的类型转换二、为什么C需要四种类型转换三、C强制类型转换1、static_cast2、reint…

【必看】Onlyfans如何使用搜索功能?Onlyfans如何搜索博主?如何在OnlyFans搜索HongkongDoll

1. 什么是Onlyfans OnlyFans是一种内容订阅服务平台,它成立于2016年。 它允许内容创作者在平台上面分享自己的创作,如图片、视频等等,用户需要支付订阅费用才能查看创作者的内容。此外,用户还可以通过打赏的方式来让创作者为自己…

[Python进阶] 制作动态二维码

11.1 制作动态二维码 二维码(QR code)是一种二维条形码(bar code),它的起源可以追溯到20世纪90年代初。当时,日本的汽车工业开始使用一种被称为QR码的二维条码来追踪汽车零部件的信息。 QR码是Quick Respo…

代码随想录算法训练营Day55|392.判断子序列、115.不同的子序列

目录 392.判断子序列 思路 ​算法实现 115.不同的子序列 思路 算法实现 总结 392.判断子序列 题目链接 文章链接 思路 利用动规五部曲进行分析: 1.确定dp数组及其下标含义: dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的…

controlnet的模型下载

controlnet模型有sd15和基于sd15上的fp16版本 fp16版本的模型比较小,但功能效果跟sd15是一样的 controlnet的fp16模型下载地址 https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/tree/main controlnet的openpose里,有个dw_open…

寒假作业

手写盗版微信登入界面 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);this->resize(421,575);this->setFixedSize(421,575);th…

AI:126-基于深度学习的人体情绪识别与分析

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

docker常用容器命令

首先说下容器: 它是指当docker运行镜像时,创建了一个隔离环境,称之为 容器。 这种方式优点:可以开启多个服务,服务之前是互相隔离的(比如:在一台服务器上可以开启多个mysql,可以是…

2.12 分支、循环练习

1、选择题 1.1、以下程序的输出结果是 A 。 main() { int k11,k22,k33,x15; if(!k1) x--; else if(k2) if(k3) x4; else x3; printf(“x%d\n”,x); } A x4 B x15 C x14 D x3 解析:if(!k1) x--; 检查 k1 是否为0。因为 k1 的值为1,所…

论文阅读-One for All : 动态多租户边缘云平台的统一工作负载预测

论文名称:One for All: Unified Workload Prediction for Dynamic Multi-tenant Edge Cloud Platforms 摘要 多租户边缘云平台中的工作负载预测对于高效的应用部署和资源供给至关重要。然而,在多租户边缘云平台中,异构的应用模式、可变的基…

Netty Review - NioEventLoopGroup源码解析

文章目录 概述类继承关系源码分析小结 概述 EventLoopGroup bossGroup new NioEventLoopGroup(1); EventLoopGroup workerGroup new NioEventLoopGroup();这段代码是在使用Netty框架时常见的用法,用于创建两个不同的EventLoopGroup实例,一个用于处理连…

pycharm控制STM32F103ZET6拍照并上位机接收显示(OV7670、照相机、STM32、TFTLCD)

基于STM32的照相机 准备工作最终效果一、下位机1、主函数2、OV7670初始化 二、上位机1、控制拍照2、接收图片数据 三、资源获取 准备工作 一、硬件及片上资源: 1,串口1(波特率:921600,PA9/PA10通过usb转ttl连接电脑,或者其他方法)上传图片数据至上位机 2,串口2(波特…

文件包含知识点详细总结

如果想看图片和观感更好的话,可以直接去我的github或者gitbook github:https://github.com/kakaandhanhan/cybersecurity_knowledge_book-gitbook.22kaka.fun gitbook:http://22kaka.fun description: 这里将通过参考文章和做题一起进行总结,并且文件包含漏洞,很多都利用了…

【JavaEE】_CSS常用属性

目录 1. 字体属性 1.1 设置字体家族 font-family 1.2 设置字体大小 font-size 1.3 设置字体粗细 font-weight 1.4 设置字体倾斜 font-style 2. 文本属性 2.1 设置文本颜色 color 2.2 文本对齐 text-align 2.3 文本装饰 text-decoration 2.4 文本缩进 text-indent 2.…

PHP特性知识点总结

如果想观感更好看到图片,可以去我的gitbook或者github去看 github:https://github.com/kakaandhanhan/cybersecurity_knowledge_book-gitbook.22kaka.fun gitbook:http://22kaka.fun description: 专门出的关于php的特性比较,后面好像也有java的特性。 🏀 PHP特性知识点…

【hcie-cloud】【26】华为云Stack主机安全防护

文章目录 前言主机安全概述主机安全概念主机安全风险与挑战 - 黑客入侵安全风险管理难安全合规审查严格 主机安全服务HSS详述企业主机安全服务介绍主机安全服务 - 实现原理(主机安全)主机安全服务 - 实现原理(容器安全)主机安全服…

安装faiss环境教程

文章目录 打开环境安装faiss环境检查已安装的环境切换环境至faiss 打开环境 source activate # 打开环境安装faiss环境 conda create -n faiss_env # 安装faiss环境检查已安装的环境 conda info --envs # 检查已安装的环境切换环境至faiss conda a…