await、async、事件循环(宏任务、微任务队列执行顺序)

news2024/9/24 19:24:38

1 async、await

2 浏览器进程、线程

3 宏任务、微任务队列

4 Promise面试题解析

5 throw、try、catch、finally

异步函数-异步函数的写法

 // 普通函数
    // function foo() {}
    // const bar = function() {}
    // const baz = () => {}

    // 生成器函数
    // function* foo() {}

    // 异步函数
    async function foo() {
      console.log("foo function1")
      console.log("foo function2")
      console.log("foo function3")
    }
    foo()

    // const bar = async function() {}
    // const baz = async () => {}
    // class Person {
    //   async running() {}
    // }

异步函数-异步函数返回值

// 返回值的区别
    // 1.普通函数
    // function foo1() {
    //   return 123
    // }
    // foo1()

    // 2.异步函数
    async function foo2() {
      // 1.返回一个普通的值
      // -> Promise.resolve(321)
      return ["abc", "cba", "nba"]

      // 2.返回一个Promise
      // return new Promise((resolve, reject) => {
      //   setTimeout(() => {
      //     resolve("aaa")
      //   }, 3000)
      // })

      // 3.返回一个thenable对象
      // return {
      //   then: function(resolve, reject) {
      //     resolve("bbb")
      //   }
      // }
    }

    foo2().then(res => {
      console.log("res:", res)
    })

异步函数-异步函数的异常

在async里面的代码如果有报错和异常,那么不会影响后续代码,但是结果会放到promise的reject里面传递出来。

 // "abc".filter()

    // 什么情况下异步函数的结果是rejected

    // 如果异步函数中有抛出异常(产生了错误), 这个异常不会被立即浏览器处理
    // 进行如下处理: Promise.reject(error)
    async function foo() {
      console.log("---------1")
      console.log("---------2")
      // "abc".filter()
      throw new Error("coderwhy async function error")
      console.log("---------3")

      // return new Promise((resolve, reject) => {
      //   reject("err rejected")
      // })

      return 123
    }

    // promise -> pending -> fulfilled/rejected
    foo().then(res => {
      console.log("res:", res)
    }).catch(err => {
      console.log("coderwhy err:", err)
      console.log("继续执行其他的逻辑代码")
    })

异步函数-await关键字使用

await如果单纯用在普通变量和普通函数里面是没什么用的,它是用来配合promise来一起使用的,作用和yield差不多,就是等到promise有返回之后再接收结果然后再执行await下面几行的代码(就是会打断代码执行)。

// 1.普通函数
    // function foo1() {
    //   await 123
    // }
    // foo1()


    // 2.await关键字
    // await条件: 必须在异步函数中使用
    function bar() {
      console.log("bar function")
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(123)
        }, 100000)
      })
    }

    async function foo() {
      console.log("-------")
      // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
      const res1 = await bar()
      console.log("await后面的代码:", res1)
      const res2 = await bar()
      console.log("await后面的代码:", res2)

      console.log("+++++++")
    }

    foo()

异步函数-await处理异步请求

在promise返回的是reject的时候,用catch可以捕获异常和错误。或者在getdata里面写try catch。

 function requestData(url) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(url)
          // reject("error message")
        }, 2000);
      })
    }

    async function getData() {
      const res1 = await requestData("why")
      console.log("res1:", res1)

      const res2 = await requestData(res1 + "kobe")
      console.log("res2:", res2)
    }

    getData().catch(err => {
      console.log("err:", err)
    })

异步函数-await和async结合

普通函数返回promise、但是异步async关键字创建的函数也一样可以在调用时候使用await。

  // 1.定义一些其他的异步函数
    function requestData(url) {
      console.log("request data")
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(url)
        }, 3000)
      })
    }

    async function test() {
      console.log("test function")
      return "test"
    }

    async function bar() {
      console.log("bar function")

      return new Promise((resolve) => {
        setTimeout(() => {
          resolve("bar")
        }, 2000);
      })
    }

    async function demo() {
      console.log("demo function")
      return {
        then: function(resolve) {
          resolve("demo")
        }
      }
    }


    // 2.调用的入口async函数
    async function foo() {
      console.log("foo function")

      const res1 = await requestData("why")
      console.log("res1:", res1)

      const res2 = await test()
      console.log("res2:", res2)

      const res3 = await bar()
      console.log("res3:", res3)

      const res4 = await demo()
      console.log("res4:", res4)
    }

    foo()

单线程-代码顺序如何执行

每个浏览器打开的页面都会成为一个进程,一个进程里面会有很多线程,但是执行JavaScript的线程只有一个。

 let name = "why"
    name = "kobe"

    function bar() {
      console.log("bar function")
    }

    function foo() {
      console.log("foo function")
      // 1.在JavaScript内部执行
      // let total = 0
      // for (let i = 0; i < 1000000; i++) {
      //   total += i
      // }

      // 2.创建一个定时器
      setTimeout(() => {
        console.log("setTimeout")
      }, 10000);

      bar()
    }

    foo()

单线程-异步代码如何执行

不管定时器定的时间是多少,都会先执行定时器后面的代码。

 事件队列,事件循环。

定时器、dom监听、网络请求都会被加到事件队列里面,然后事件队列按照先进先出的顺序把事件输入到执行上下文中。

 

<body>

  <button>按钮</button>
  
  <script>

    const btn = document.querySelector("button")
    btn.onclick = function() {
      console.log("btn click event")
    }

    console.log("Hello World")
    let message = "aaaa"
    message = "bbbb"

    setTimeout(() => {
      console.log("10s后的setTimeout")
    }, 0);

    console.log("Hello JavaScript")
    console.log("代码继续执行~~~")
    console.log("-------------")

  </script>

</body>

单线程-微任务和宏任务区别

promise里面的函数和写在外面的函数一样,都是马上就执行,没有放到事件队列中。但是如果这个promise有执行resolve或者reject时,回调函数then和catch里面的代码是会放到队列里面。

定时器的代码会被放到宏任务(macrotask)中,then里面的代码会放到微任务(microtask)当中。

宏任务和微任务的划分是规定好了的,不是我们来选择的。

 

微任务的作用就是希望比宏任务先执行,所以微任务比宏任务先执行。

  console.log("script start")

    // function bar() {
    //   console.log("bar function")
    // }

    // function foo() {
    //   console.log("foo function")
    //   bar()
    // }
    // foo()

    // 定时器
    setTimeout(() => {
      console.log("setTimeout0")
    }, 0)
    setTimeout(() => {
      console.log("setTimeout1")
    }, 0)

    // Promise中的then的回调也会被添加到队列中
    console.log("1111111")
    new Promise((resolve, reject) => {
      console.log("2222222")
      console.log("-------1")
      console.log("-------2")
      resolve()
      console.log("-------3")
    }).then(res => {
      console.log("then传入的回调: res", res)
    })
    console.log("3333333")

    console.log("script end")

代码执行顺序-面试题一(重要)

注意就是普通的函数和promise回调函数都是直接放到执行上下文中执行的,promise的resolve和reject回调的then和catch里面的代码是放到微任务中执行。

微任务比宏任务先执行

最好的办法就是把宏任务队列和微任务队列的执行顺序画出来

 

 微任务如果里面有微任务的话,宏任务就会一直延后才会执行,不管微任务是否早就执行完成。执行到宏任务时如果创建里面微任务(已经执行到宏任务了,只能说微任务已经结束了),等这一个宏任务完成之后,就立马回去执行微任务

 最终执行顺序:

 

 console.log("script start")

    setTimeout(function () {
      console.log("setTimeout1");
      new Promise(function (resolve) {
        resolve();
      }).then(function () {
        new Promise(function (resolve) {
          resolve();
        }).then(function () {
          console.log("then4");
        });
        console.log("then2");
      });
    });

    new Promise(function (resolve) {
      console.log("promise1");
      resolve();
    }).then(function () {
      console.log("then1");
    });

    setTimeout(function () {
      console.log("setTimeout2");
    });

    console.log(2);

    queueMicrotask(() => {
      console.log("queueMicrotask1")
    });

    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then3");
    });

    console.log("script end")

代码执行顺序-await代码

then如果是在宏任务中执行的,那包裹这个then的宏任务会先执行后再执行then微任务。

定时器还分时间的,时间短的先进宏任务。

 await还没拿到结果前,这个作用域内的后续代码直接不执行,跳过。并且在同一个作用域的await后面的代码是相当于then里面的代码,都是放到微任务的。

 

 console.log("script start")

    function requestData(url) {
      console.log("requestData")
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log("setTimeout")
          resolve(url)
        }, 2000);
      })
    }

    // 2.await/async
    async function getData() {
      console.log("getData start")
      const res = await requestData("why")
      
      console.log("then1-res:", res)
      console.log("getData end")
    }

    getData()
    
    console.log("script end")

    // script start
    // getData start
    // requestData
    // script end

    // setTimeout

    // then1-res: why
    // getData end

代码执行顺序-面试题二

下图执行到async2()的时候会去调用asunc2的函数,虽然只打印的是async2,但是有隐藏的return undefined,相当于resolve(undefined),而console.log('async1 end')这行会被加入到微任务里面,因为相当于被放到then里面了。

 注意:await async2()的代码会和console.log('async1 start')一样当做普通代码执行,而await async2()是个函数,要找到那个函数然后跳到这个函数里面执行里面的代码。

 

 

  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')

异常处理-默认错误处理

如果是浏览器报错,那出错的源码那行之后的代码都不会执行了。所以很危险。

解决办法之一是主动抛出异常,使用throw关键字。但是throw后面的代码也不会执行,好处是可以抛出你自定义的问题描述。

 

 // 1.遇到一个错误, 造成后续的代码全部不能执行
    // function foo() {
    //   "abc".filter()

    //   console.log("第15行代码")
    //   console.log("-------")
    // }

    // foo()
    // console.log("+++++++++")

    // const btn = document.querySelector("button")
    // btn.onclick = function() {
    //   console.log("监听btn的点击")
    // }

    // 2.自己封装一些工具
    function sum(num1, num2) {
      if (typeof num1 !== "number") {
        throw "type error: num1传入的类型有问题, 必须是number类型"
      }

      if (typeof num2 !== "number") {
        throw "type error: num2传入的类型有问题, 必须是number类型"
      }

      return num1 + num2
    }

    // 李四调用
    const result = sum(123, 321)

异常处理-throw抛出异常

抛出异常的信息是一句话的话没什么效果,一般会使用对象类型来抛出去,能显示的内容跟多。当然也可以自定义class来写错误信息。

系统也有已经写好的Error的类可以直接使用。 

  class HYError {
      constructor(message, code) {
        this.errMessage = message
        this.errCode = code
      }
    }

    // throw抛出一个异常
    // 1.函数中的代码遇到throw之后, 后续的代码都不会执行
    // 2.throw抛出一个具体的错误信息
    function foo() {
      console.log("foo function1")
      // 1.number/string/boolean
      // throw "反正就是一个错误"

      // 2.抛出一个对象
      // throw { errMessage: "我是错误信息", errCode: -1001 }
      // throw new HYError("错误信息", -1001)

      // 3.Error类: 错误函数的调用栈以及位置信息
      throw new Error("我是错误信息")

      console.log("foo function2")
      console.log("foo function3")
      console.log("foo function4")
    }

    function bar() {
      foo()
    }

    bar()

 

异常处理-异常的捕获方式

为什么一旦出现报错,后续代码都不执行了呢?

是因为从报错的地方层层向上报错,最终会在浏览器中抛出错误,所以后续代码就中断执行了,如果我们又写捕获处理错误的代码,那后续代码就可以继续执行。

finally是一定会执行的

 function foo() {
      console.log("foo function1")
      // throw new Error("我是错误信息")
      console.log("foo function2")
      console.log("foo function3")
      console.log("foo function4")
    }

    function test() {
      // 自己捕获了异常的话, 那么异常就不会传递给浏览器, 那么后续的代码可以正常执行
      try {
        foo()
        console.log("try后续的代码")
      } catch(error) {
        console.log("catch中的代码")
        // console.log(error)
      } finally {
        console.log("finally代码")
      }
    }

    function bar() {
      test()
    }

    bar()

    console.log("--------")

写了try catch捕获异常后的结果,后续代码可以正常执行。

 

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

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

相关文章

效果超过deepsort,yolov5+bytetrack

目录 1. Motivation 2. BYTE 3. ByteTrack 4.完整代码实现 ByteTrack: Multi-Object Tracking by Associating Every Detection Box 沿着多目标跟踪&#xff08;MOT&#xff09;中tracking-by-detection的范式&#xff0c;我们提出了一种简单高效的数据关联方法BYTE。 利用…

git : 从入门到进阶(实战问题对策)

目录 0. 前言 1. git stash: 暂时保存本地修改 0. 前言 记录日常git使用过程中碰到的一些常见问题的解决&#xff0c;以及一些常用技巧。作为自己作为git使用者的从入门到进阶的成长过程。不求完备但求简洁实用。动态更新。。。 1. git stash: 暂时保存本地修改 多人工作的项…

Linux自主学习 - 多线程的创建(#include<pthread.h>)

备注&#xff1a;vscode通过ssh连接虚拟机中的ubuntu&#xff0c;ubuntu-20.04.3-desktop-amd64.iso 函数pthread_create() // pthread.h中的函数pthread_create()extern int pthread_create (pthread_t *__restrict __newthread, // 线程标识符const pthread_attr_t *…

【039】掌握Vector容器:C++中最强大的动态数组

掌握Vector容器&#xff1a;C中最强大的动态数组 引言一、vector容器概述二、vector的数据结构三、vector常用的API操作3.1、vector构造函数3.2、vector常用的赋值操作3.3、vector的大小操作3.4、vector存取数据操作3.5、vector插入和删除操作 四、vector的未雨绸缪机制五、巧用…

数据库应用:CentOS 7离线安装PostgreSQL

目录 一、理论 1.PostgreSQL 2.PostgreSQL离线安装 3.PostgreSQL初始化 4.PostgreSQL登录操作 二、实验 1.CentOS 7离线安装PostgreSQL 2.登录PostgreSQL 3.Navicat连接PostgreSQL 三、总结 一、理论 1.PostgreSQL &#xff08;1&#xff09;简介 PostgreSQL 是一个…

vue3 -- mitt 插件使用

介绍 mitt插件是Vue3中的一种第三方总线插件,它可以用于在组件之间进行通信。相比于Vue实例上的EventBus,mitt.js足够小,仅有200bytes,支持全部事件的监听和批量移除,它还不依赖Vue实例,所以可以跨框架使用,React或者Vue,甚至jQuery项目都能使用同一套库 . 使用 1:下载插…

Interactive Image Segmentation

Focused and Collaborative Feedback Integration for Interactive Image Segmentation CVPR 2023 清华 Interactive image segmentation aims at obtaining a segmentation mask for an image using simple user annotations. During each round of interaction, the segment…

windows打开此类文件前总是询问怎么解决

打开此类文件前总是询问怎么解决这个一直提示的问题呢&#xff1f; 下面来教大家一个方法杜绝再提示&#xff1a; 开始 --> 运行 --> gpedit.msc (组策略) --> 用户配置 --> 管理模板 --> windows组件 --> 附件管理器 --> 右击 "中等危险文件类型的包…

再学JavaScript

九、常见的运算符 两个等号只判断值&#xff0c;三个等号判断值和类型是否相等 逻辑运算符 注意&&和& ||和| 短路 赋值运算符 自加自减运算符 三目运算符 移位运算符 十、JavaScript的数据类型转换 假如用默认值10&#xff0c;控制台结果就是1035&#xff08…

学生管理系统--java+mysql

学生管理系统 简介 练习&#xff1a;完成学生信息的增删查改&#xff08;根据id&#xff09;&#xff0c;项目进行了三层架构进行创建。 pojo层&#xff0c;dao层&#xff0c;service层&#xff0c;utils层&#xff0c;程序入口&#xff1b; 1.pojo层 实体层 数据库在项目…

基于linux下的高并发服务器开发(第一章)- dup,dup2函数

int dup(int oldfd);复制文件描述符 /*#include <unistd.h>int dup(int oldfd);作用&#xff1a;复制一个新的文件描述符fd3, int fd1 dup(fd),fd指向的是a.txt, fd1也是指向a.txt从空闲的文件描述符表中找一个最小的&#xff0c;作为新的拷贝的文件描述符*/#include &…

80C51定时/计数器的应用之实现PWM(脉冲宽度调制)

知识来源于链接&#xff1a;https://www.bilibili.com/video/BV1eT4y1J7wB/?spm_id_from333.880.my_history.page.click&vd_sourceb91967c499b23106586d7aa35af46413 这种模拟实现 PWM 波的应用只能应用于对波形精度和频率要求不高的情况下。 一、程序思路分析 这里想要…

Redis缓存雪崩、穿透、击穿原因分析和解决方案,附Redis管道使用技巧

先给大家附上其他几篇文章&#xff0c;感兴趣的自行开车导航 Redis过期策略和持久化机制全面揭秘&#xff0c;教你如何合理配置 【深入浅出Redis 一】从版本特性到数据类型到线程模型&#xff0c;带你了解Redis的核心特性和应用场景&#xff01; 一次redis OOM问题分析解决&…

PyTorch深度学习——Anaconda和PyTorch安装

一、Anaconda安装 前言 安装anaconda后主要有一下3点好处&#xff1a; 1.包含环境管理器conda。 2.大量安装基于python的工具包。 3.可以创建使用和管理不同的python版本。 附上百度百科的解释&#xff1a; 下载步骤&#xff1a; 1.官网下载anaconda 2.双击之后安装即可 …

【Linux】进程间通信——管道与共享内存

文章目录 前言 1、三个问题1-1、什么是通信&#xff1f;1-2、为什么要有通信1-3、怎么进行通信&#xff1f;1-4、进程间通信分类 2、管道2-1、匿名管道2-1-1、理解通信本质问题2-1-2、进一步理解管道2-1-3、代码实现pipe函数 2-1-4、读写特征2-1-5、管道的特点&#xff08;重点…

MQTT springboot + idea

参考链接&#xff1a;&#xff08;第一个是理论 第二个是代码 我是直接cv的 我就不贴代码了&#xff09; MQTT协议基本流程、原理_mqtt协议工作原理_Nimrod__的博客-CSDN博客 SpringBoot整合MQTT_springboot mqtt_N_P_E的博客-CSDN博客 EMQX 入门教程③——默认端口、端口策…

10.Java 基本数据类型与包装类之间的转换

Java 的包装类便是一种特殊的引用数据类型&#xff0c;因为其是与基本数据类型一一对应的 1.装箱和拆箱 装箱指的是将基本数据类型转为包装类&#xff1b;拆箱指的是将包装类转为基本数据类型 1.1 int 与 Integer 之间的转换 int → Integer int i 13; Integer integer I…

基于JSP+servlet+JDBC开发的人物管理系统

文章目录 技术说明【效果图】源码 技术说明 后端&#xff1a;JspServletJDBC 前端&#xff1a;BootStrap技术 数据库&#xff1a;Mysql 其他&#xff1a;ckeditor富文本编辑器、FileUpload组件上传图片、MD5加密技术 功能&#xff1a;人物的增删改查 【效果图】 源码 https:…

mysql、redis 、RabbitMQ只能本机访问,怎么改?

如果只能本机访问&#xff0c;怎么改? 一、mysql - 改my.ini 刷脚本 bind-address0.0.0.0 然后重启一下mysql服务 任务管理器-关掉mysql 搜索 计算机管理-重启mysql服务 然后 打开查询&#xff0c;并选择mysql数据&#xff0c;输入这个sql语句&#xff0c;点击运行 sele…

小程序首页轮播图设计

效果图 微信小程序的数据详解 indicator-dots&#xff1a;是否显示面板指示点【默认false 】 indicator-color&#xff1a;指示点颜色【默认rgba(0, 0, 0, .3)】 indicator-active-color&#xff1a;当前选中的指示点颜色【默认#000000】 autoplay&#xff1a;是否自动切换…