async/await的实现原理(手动实现)

为什么要引入async/await操作符?

对于js的异步编程场景,无论是使用xhr回调还是promise回调,当异步操作过多并且每个动作之间存在依赖关系(即需要串行执行)时,代码的可读性和维护性会变得很差。async/await以同步代码的形式很好地解决了这个问题。

因为async/await的底层实现使用generator和promise,所以首先要了解generator的原理。

生成器函数(generator)

function* genDemo() {console.log("开始执行第一段")yield 'generator 2'console.log("开始执行第二段")yield 'generator 2'console.log("开始执行第三段")yield 'generator 2'console.log("执行结束")return 'generator 2'
}console.log('main 0')
let gen = genDemo()
console.log(gen.next().value)
console.log('main 1')
console.log(gen.next().value)
console.log('main 2')
console.log(gen.next().value)
console.log('main 3')
console.log(gen.next().value)
console.log('main 4')

带*号的函数就叫生成器函数,是可以暂定执行和恢复执行的。

在执行过程中,如果遇到yield关键字,函数会返回关键字后面的内容给外部,然后暂停执行。

外部可以使用next方法来恢复执行该函数。

v8是如何实现函数的执行和恢复的呢?这里需要引入协程的概念。

协程

协程是一种比线程更轻量级的存在,可以理解为是在线程上运行的任务。一个线程可以存在多个协程,但同时只能执行一个协程。如果当前执行的是A协程,要启动B协程就需要暂停A协程,把js主线程的控制权交给B协程,这样表现为A协程暂停,B协程开始执行,此时我们把A协程称为B协程的父协程。

分析上面的示例代码,执行过程如下:

  1. js主线程执行genDemo方法,生成gen协程。
  2. js主线程执行gen.next方法,此时js引擎会保存当前主线程(父协程)调用栈信息,开始执行gen协程。
  3. gen协程碰上yield关键词,会保存当前gen协程的调用栈信息,并将关键词后的内容返回给父协程,并暂定执行gen协程,恢复执行父协程(即外部代码);
  4. 后续反复执行2和3的步骤,直接主线程代码执行完。

结论:

gen协程和父协程是交替执行的,它们通过yield和next配合完成切换。

async/await

async:

async function foo() {   console.log(1);   return 2
}
var p = foo();
console.log(p);
// 1
// Promise { 2 }

从以上代码可以得知,async声明后的函数返回了一个Promise对象,状态是resolved,上述代码等价于

function foo() {   console.log(1);    return new Promise(resolve => resolve(2))
}
var p = foo();
console.log(p);

await:

async function foo() {    console.log(1)    let a = await 100    console.log(a)    console.log(2)
}
console.log(0)
foo()
console.log(3)
// 0
// 1
// 3
// 100
// 2

分析执行过程:

  1. 输出0;
  2. 主线程保存父协程调用栈信息,开始执行foo协程,输出1;
  3. 遇到await操作符,await操作符干了很多事情。
    let a = await 100;首先相当于执行了new Promise(resolve => resolve(100));
    然后将主线程控制权交给父协程,同时将该promise对象返回给父协程;
  4. 父协程调用promise对象的then方法,监控promise状态的改变。
  5. 输出3,宏任务队列执行完成;
  6. 检查微任务队列,执行promise对象的then方法回调,并将返回值传给foo协程,并执行foo协程;
  7. 输出100,输出2;
  8. 完成;

手动实现async、await

​​​​​​​

1. 创建启动函数,**传入生成器函数**(判断传入值是否生成器函数)

2. 创建执行函数(**接收一个参数**,每次 next 后的返回值 => { value , done })

- 通过判断 v.done 决定递归是否结束

- 递归执行自身,并判断返回值,如果是 **promise对象** 就调用 .then 等待异步执行后,执行 next 并将 promise 结果传入;如果是**基本类型**就直接执行 next 函数,传入 value 值即可

3. **执行启动函数**,传入(generator.next())


// promise 函数
function getData (data, time = 1000) {// 返回 promise 对象if (typeof data === 'object') {return new Promise(function (resolve, reject) {setTimeout(function () {resolve(data)}, time)})}// 返回普通类型else {return data}
}// function* => async function
function* test () {let res1 = yield getData("字符串");     // yield => awaitconsole.log("res1", res1);let res2 = yield getData({ data: [1, 2, 3] });console.log("res2", res2);let res3 = yield getData({ data: { a: 1 } }, 3000);console.log("res3", res3);let res4 = yield getData(true);console.log("res4", res4);
}// async await 底层原理:启动一个自执行的生成器函数
function start (fn) {// 1 判断是否传入生成器函数if (fn.constructor.name != 'GeneratorFunction') {throw new Error('it is not a generator function.')}// 2 执行获取生成器对象let generator = fn()// 3 创建一个执行函数(val => 执行 next 函数后的值)let execute = (val) => {// 1 递归结束if (val.done) return;// 2 判断类型if (val.value instanceof Promise) {// 3 递归执行val.value.then(res => {execute(generator.next(res))}, err => {generator.throw(err)})} else {// 3 递归执行Promise.resolve(val.value).then(res => {execute(generator.next(res))})}}// 4 启动执行函数execute(generator.next())
}console.log(1);
start(test)
console.log(2);


 

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

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

相关文章

华为OD机试真题-两个字符串间的最短路径问题-2023年OD统一考试(C卷)

题目描述: 给定两个字符串,分别为字符串A与字符串B。例如A字符串为ABCABBA,B字符串为CBABAC可以得到下图m*n的二维数组,定义原点为(0, 0),终点为(m, n),水平与垂直的每一条边距离为1,映射成坐标系如下图。 从原点(0, 0)到(0, A)为水平边,距离为1,从(0, A)到(A, C)为垂…

Selenium page object模式Python

目录 概述 优点 示例 项目结构: 基础页面类BasePage 业务页面类BaiduHomePage 测试类test_baidu: 文件工具类file_util 运行日志: 测试结果: 概述 在web应用程序的UI中,有一些区域可以与测试交互。页面对象…

数据表没有主键进行自关联

需求: 大数据环境,特别的hive的表,很多是没有主键的。有时候sql查询的时候,需要自关联,这就需要根据数据采用不同的方案 自关联 数据表test(id,name,age),如果有主键id,自关联sql如下 select * from test…

acwing算法基础之贪心--排序不等式、绝对值不等式和推公式

目录 1 基础知识2 模板3 工程化 1 基础知识 暂无。。。 2 模板 暂无。。。 3 工程化 题目1:排队打水。给定N个数,表示装满每个桶所需的时间,请你安排打水顺序,使得等待时间最小,并输出该最小值。 解题思路&#…

Kubernetes sample-controller 例子介绍

sample-controller sample-controller 是 K8s 官方自定义 CDR 及控制器是实现的例子 通过使用这个自定义 CDR 控制器及阅读它的代码,基本可以了解如何制作一个 CDR 控制器 CDR 运作原理 网上有更好的文章,说明其运作原理: https://www.z…

【虚拟机】Docker基础 【二】

2.2.数据卷 容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便。大家思考几个问题: 如果要升级MySQL版本,需要销毁旧容器,那么数据岂不是跟着被销毁了&#x…

微信小程序实现打分效果代码整理

一、微信小程序点击对应点击高亮 js代码 Page({data: {list: [1, 2, 3, 4, 5],active: 0},itemClickOne(e){var nume.currentTarget.dataset.value;this.setData({active:num});}, }) wxml代码 <view class"list"><view class"item {{itemactive?…

IDC MarketScape2023年分布式数据库报告:OceanBase位列“领导者”类别,产品能力突出

12 月 1 日&#xff0c;全球领先的IT市场研究和咨询公司 IDC 发布《IDC MarketScape:中国分布式关系型数据库2023年厂商评估》&#xff08;Document number:# CHC50734323&#xff09;。报告认为&#xff0c;头部厂商的优势正在扩大&#xff0c;OceanBase 位列“领导者”类别。…

C#语言高阶开发

目录 数据结构 集合 动态数组ArrayList 习题&#xff1a;声明一个Monster类&#xff0c;有一个Attack方法,用一个ArrayList去封装Monster的对象,装10个&#xff0c;遍历monster的list让他们释放攻击方法 哈希表HashTable 创建一个武器类&#xff0c;有一个属性叫做id,每个…

【数据中台】开源项目(3)-Linkis

关于 Linkis Linkis 在上层应用程序和底层引擎之间构建了一层计算中间件。通过使用Linkis 提供的REST/WebSocket/JDBC 等标准接口&#xff0c;上层应用可以方便地连接访问MySQL/Spark/Hive/Presto/Flink 等底层引擎&#xff0c;同时实现统一变量、脚本、用户定义函数和资源文件…

web:very_easy_sql(sql、ssrf、gopher协议sql注入)

题目 页面显示如下 显示不是内部用户&#xff0c;无法识别信息 查看源码&#xff0c;找到一个use.php 访问之后显示如下 随便输入了一个&#xff0c;发现url有参数显示 试一下靶机的网址&#xff0c;返回nonono 联系之前原始页面写的“不是内网用户&#xff0c;无法别识身份”…

【PTA-C语言】实验三-循环结构I

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 实验三-循环结构I 7-1 求交错序列前N项和 &#xff08;分数 15&#xff09;7-2 寻找250&#xff08;分数 15&#xff09;7-3 最大公约数和最小公倍数&#xff08;分数 15&#xff09;7-4 统计字符&#xff0…

Redis 发布订阅机制深入探索

Redis 的发布订阅&#xff08;pub/sub&#xff09;机制是一种消息传递模式&#xff0c;允许消息的发送者&#xff08;发布者&#xff09;和消息的接收者&#xff08;订阅者&#xff09;通过一个中介层&#xff08;频道&#xff09;进行通信&#xff0c;而无需彼此直接交互。以下…

C到C++过渡

C到C过渡 函数提高函数重载函数模板有默认参数的函数 结构体与类结构体类对象封装 函数提高 函数重载 C允许用同一函数名定义多个函数, 而这些函数的参数个数和参数类型可以不相同。这就是函数重载。即对一个函数名重新赋予它新的含义使一个函数名可以多用。所谓重载,其实就是…

231202 刷题日报

周四周五&#xff0c;边值班边扯皮&#xff0c;没有刷题。。 今天主要是做了: 1. 稀疏矩阵压缩&#xff0c;十字链表法 2. 快速排序 3.349. 两个数组的交集​​​​​ 4. 174. 地下城游戏 要注意溢出问题&#xff01;

外包搞了6年,技术退步明显......

先说情况&#xff0c;大专毕业&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

vue项目报错及解决npm run build:prod打包错误

vue项目报错及解决npm run build:prod打包错误 执行dev环境时加载失败了该变量&#xff0c;在package.json文件中 删掉 解决方法&#xff1a; 打包成功&#xff1a;

使用 OpenFunction 在任何基础设施上运行 Serverless 工作负载

作者&#xff1a; 霍秉杰&#xff1a;KubeSphere 可观测性、边缘计算和 Serverless 团队负责人&#xff0c;Fluent Operator 和 OpenFunction 项目的创始人&#xff0c;还是多个可观测性开源项目包括 Kube-Events、Notification Manager 等的作者&#xff0c;热爱云原生技术&am…

Hdoop学习笔记(HDP)-Part.16 安装HBase

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

MathType 7.5.2中文版软件使用期到了怎么办?

MathType 7.5.2中文版作为一款专业的公式编辑器&#xff0c;MathType受到很多人的青睐&#xff0c;它可以将编辑好的公式保存成多种图片格式或透明图片模式&#xff0c;可以很方便的添加或移除符号、表达式等模板&#xff08;只需要简单地用鼠标拖进拖出即可)&#xff0c;也可以…