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

这篇具有很好参考价值的文章主要介绍了重学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执行的过程中,遇见了定时器等相关事件,又会怎么操作
    • 首先会执行函数,但是不会执行里面的回调函数
    • 将里面的回调函数交由浏览器其他线程
    • 其他线程在处理好之后,将回调函数放入事件队列中
    • 待执行栈中空了,就会从事件队列中取出待执行的函数

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

微任务和宏任务

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

  • 宏任务队列: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捕获异常文章来源地址https://www.toymoban.com/news/detail-781358.html

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

到了这里,关于重学JavaScript高级(十二):async/await-事件循环-面试高频的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 深入理解Async/Await:从原理到实践的JavaScript异步编程指南

    深入理解Async/Await:从原理到实践的JavaScript异步编程指南

    理解 async/await 的原理和使用方法是理解现代JavaScript异步编程的关键。这里我会提供一个详细的实例,涵盖原理、流程、使用方法以及一些注意事项。代码注释会尽量详尽,确保你理解每个步骤。 实例:使用async/await进行异步操作 详细解释和注释: 异步函数定义: async func

    2024年02月05日
    浏览(19)
  • 前端面试:【异步编程】Callback、Promise和Async/Await

    嗨,亲爱的JavaScript探险家!在JavaScript开发的旅程中,你会经常遇到异步编程的需求。为了处理异步操作,JavaScript提供了多种机制,包括Callbacks、Promises和Async/Await。本文将深入介绍这些机制,让你能够更好地处理异步任务。 1. Callbacks:传统的异步方式 Callbacks是JavaScript中最早

    2024年02月11日
    浏览(54)
  • 一盏茶的功夫帮你彻底搞懂JavaScript异步编程从回调地狱到async/await

    一盏茶的功夫帮你彻底搞懂JavaScript异步编程从回调地狱到async/await

     🎬 江城开朗的豌豆 :个人主页  🔥  个人专栏   :《 VUE 》 《 javaScript 》 ⛺️  生活的理想,就是为了理想的生活 ! 目录 📘 1. 引言 📘 2. 使用方法 📘 3. 实现原理 📘 4. 写到最后   在深入讨论 async/await 之前,我们需要了解一下 JavaScript 的单线程和非阻塞的特性。

    2024年02月08日
    浏览(14)
  • Ajax_4(进阶)同步异步+ 宏任务微任务 + Promise链 + async终极解决方案 +事件循环原理 + 综合案例

    01-同步代码和异步代码 什么是同步代码? 同步代码:逐行执行,需要原地等待结果后,才继续向下执行。 什么是异步代码? 调用后耗时,不阻塞代码继续执行,(不必原地等待),在将来完成后 触发一个 回调函数 。 代码阅读 目标:阅读并回答代码执行和打印的顺序 打印

    2024年02月13日
    浏览(34)
  • JavaScript中的事件循环机制,包括事件循环的原理、宏任务和微任务、事件队列和调用栈、以及如何优化事件循环

    JavaScript中的事件循环机制是JavaScript运行引擎的核心之一,它决定了代码的执行方式和效率。本文将从几个方面介绍JavaScript中的事件循环机制,包括事件循环的原理、宏任务和微任务、事件队列和调用栈、以及如何优化事件循环。 一、事件循环的原理 事件循环是JavaScript中实

    2024年02月05日
    浏览(11)
  • 记录在JavaScript中对事件循环的理解

    好的,用更通俗的话来说,事件循环就像是在一个大剧院里,有一个演员(JavaScript引擎)和两个重要的角色:一个是前台的表演者(调用栈),另一个是后台的候场区(事件队列)。 前台表演者: 这个演员在前台表演,一次只能表演一个节目(单线程执行)。当一个节目(

    2024年04月22日
    浏览(16)
  • async/await 编程理解

    async/await 编程理解

    博客参考 Asynchronous Programming in Rust ,并结合其中的例子阐述 async 和 await 的用法,如何使用 async 和 await 是本节的重点。 async 和 await 主要用来写异步代码,async 声明的代码块实现了 Future 特性。如果实现 Future 的代码发生阻塞,会让出当前线程的控制权,允许线程去执行别的

    2024年02月12日
    浏览(16)
  • async/await 的用法

    使用场景 在实际开发中,如果你遇到了等第一个请求返回数据完,再执行第二个请求(可能第二个请求要传的参数就是第一个请求接口返回的数据)这个问题。 代码 方法1: 方法2: 先请求接口1,获取到接口1返回结果后,将其作为接口2的参数,再去请求接口2 1、async 用于申明

    2024年02月07日
    浏览(16)
  • async和await

    Node.JS官方文档:https://nodejs.dev/en/ 创建异步函数,并返回相关数值: 一般方式创建 通过async方式创建: 在async声明的函数中可以使用await来调用异步函数 当我们通过await去调用异步函数时候,它会暂停代码的运行 直到异步代码执行有结果时,才会将结果返回 注意 awa

    2024年02月02日
    浏览(11)
  • Promise、Async/Await 详解

            Promise是抽象异步处理对象以及对其进行各种操作的组件。Promise本身是同步的立即执行函数解决异步回调的问题, 当调用 resolve 或 reject 回调函数进行处理的时候, 是异步操作, 会先执行.then/catch等,当主栈完成后,才会去调用执行resolve/reject中存放的方法。      

    2024年02月14日
    浏览(20)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包