【js】js 异步机制详解 Generator / Async / Promise
三种语法功能放在一起,是因为他们都有相似特点:
- 维护某种状态
- 在未来恢复状态并执行
本文重点回答以下几个问题:
- 为什么 Generator 和 Async 函数的 代码执行流 都可以简化成树形结构?
- async 函数为什么返回一个 promise?返回了怎样一个 promise?
- async 函数如何优雅的转换成 promise 函数?
Generator 用法和思考
基本生成器
generator 是生成器,从生成器的行为来看,它就是一个迭代器。
function* foo(end) {var idx = 0;while (idx < end) {yield idx; // 保存当前状态并返回 当前idxidx += 5;}
}const iterator = foo(20);
// iterator.next().value 手动迭代
for (let val of iterator) { // 输出 0 到 19console.log(val);
}
对应到生成器函数中,他会在 yield 的时候保存当前函数的状态。那么,函数的状态保存在哪? 习惯了 C 函数的我们很难想象如何将状态保存在函数中。可能协程和 setjmp/longjmp 可以辅助我们思考生成器的实现,但还是不能直接对应到生成器函数的行为。比如:生成器需要记录如下信息
- Idx 的大小, end 大小
- pc 指针在 第 5 行
想在 c 语言中保存上述信息是不容易的。是因为函数在 js 是第一类值,上述的信息便可以保存在函数值本身内。对应quickjs 相当于将 pc 指针 和 参数 信息,local_buf,保存在对应的函数对象 (实际上保存在 StackFrame 中) 内,而 cur_ref 信息 quickjs 本身就已经保存了。而 c/c++ 语言的函数没有类似功能。
嵌套多层迭代器
- 树形生成器结构。讲
yield这种语法,是因为它跟async的执行流非常像,但会可以看到 - 使用树形生成器生成 0 ~ 20 的值

function* inner(index) {var i = 0;while (i < 5) {yield index + i;i++;}
}// 错误的生成器用法,但是启发我们思考,这样的用法,是不是很像 await?
// function* foo(index) {
// while (index < 20) {
// yield inner(index);
// index += 5;
// }
// }// 正确版本
function* foo1(index) {while (index < 20) {var it = inner(index);for (let val of it) {yield val;}index += 5;}
}// 语法糖版本
function* foo2(index) {while (index < 20) {yield *inner(index);index += 5;}
}const iterator = foo1(0);for (let val of iterator) {console.log(val);
}
async 执行流的思考
-
Async 的代码执行流程是下面这棵树的中序遍历
-
Async 函数的代码执行流程如下
- 先执行绿色部分代码,直到最底层的 promise,由于promise 测试未决定(pending 状态),整个函数暂停退出。
- promise resolve或reject 后,恢复中序遍历执行淡蓝色部分代码,直到下一个 promise。这里相当于执行了 promise 的 then 回调。
- Plus. 这里仍然有一次 暂停执行。因为 新的 promise 是 pending 状态。
- 新的 promise resolve或reject 后,继续恢复执行 。。。。
- Async 函数的结尾的 return 道理上相当于一个 then 回调
-
Async 函数的代码执行流程非常像 树形 generator
-
都说 async 函数返回一个 promise,它返回的 promise 到底是什么?

-
Async 函数框架
async function asyncfunc() {code1;var a1 = await asyncfunc1(); // 这里是一个 async 函数code2;var a2 = await promise1; // 这里是一个 真正的 Promisecode3;var a3 = await asyncfunc2(); // 这里是一个 async 函数codeR;return x;
}asyncfunc();
- 可以尝试使用如下代码,单步调试来验证
async function wrapFetch(url) {console.log("wrapFetch code1");const response = await fetch(url);console.log("wrapFetch code2");return response;
}// 异步函数示例
async function fetchAsyncData() {console.log("fetchAsyncData code1");const response = await wrapFetch('https://qqlykm.cn/api/weather/get');console.log("fetchAsyncData code2 ok?", response.ok);const data = await response.json();console.log(data);
}// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");
- 问题:能把 async 函数串行化么?答案是不能。只要使用了
promise(叶子节点是真正的 promise),代码的执行就要遵循 promise 的执行规则,而 promise 本身就是异步的,因此无法串行化。
一点点 Promise 的思考
想要理解如何将 async 函数返回的到底是什么 promise?以及如何将 async 函数翻译成一个 promise 的形式,需要深入理解 then 函数。
Then 函数
- Then 函数返回一个 promise,称其为 p。
- Then 注册的回调函数中也会返回一个值。那么 返回 一个值 和 返回一个 promise 有什么不同?
- 如果回调函数返回了 promise,那么这个promise 和 then 返回的 promise p 是 什么关系?官方文档:链式调用。
- 结论:他俩在行为上是 同一个 promise
- 知道这一点,就可以轻松的把一个 async 函数改写成 promise 的形式了。
const myPromise = new Promise((resolve, reject) => {// 进行异步操作const randomNumber = Math.random();// 如果操作成功,调用resolve并传递结果resolve(randomNumber);
});// 使用Promise对象
myPromise.then(result => {// 操作成功时的处理逻辑console.log("操作成功,结果为:" + result);**return 1; ****return new Promise((rs, rj) => rs(2));**}).then( val => {console.log(val);})
将 async 改写成 promise
如何改写?下面用一个示例来展示
注:这里并没有关注异常执行流,而是只关注异步执行流。异常执行流要想完全转换,需要在promise里注册错误回调,并且 async 和 promise 都要捕获异常
// async 写法async function asyncfunc() {code1;var a1 = await asyncfunc1(); // 这里是一个 async 函数code2;var a2 = await promise1; // 这里是一个 真正的 Promisecode3;var a3 = await asyncfunc2(); // 这里是一个 async 函数codeR;return x;
}asyncfunc();
// 等价的promise 写法function func() {return new Promise((resolve, reject) =>{code1;func1().then( (val) => resolve(val) );}).then((a1) => {code2;return promise1;}).then((a2)=> {code3;// 返回一个Promisereturn func2(); }).then((a3) => {codeR;return x;});
}
func();
结论:async 语法糖有什么好处呢?
- 代码写起来更简洁、优雅,意味着 减少出错率
- 词法作用域更广。写 code2 可以用 code1 部分的变量,写 promise 的时候就没办法了。
可验证代码
异步调用一个 网络 api 接口
- async 版本
async function wrapFetch(url) {console.log("wrapFetch code1");const response = await fetch(url);console.log("wrapFetch code2");return response;
}// 异步函数示例
async function fetchAsyncData() {console.log("fetchAsyncData code1");const response = await wrapFetch('https://qqlykm.cn/api/weather/get');console.log("fetchAsyncData code2 ok?", response.ok);const data = await response.json();console.log(data);
}// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");
- 等价 Promise 版本
function wrapFetch(url) {return new Promise( function (resolve, reject) {console.log("wrapFetch code1");fetch(url).then( (response) => {resolve(response);console.log("wrapFetch code2");})});
}function fetchAsyncData() {return new Promise(function (resolve, reject) {console.log("fetchAsyncData code1");wrapFetch('https://qqlykm.cn/api/weather/get').then( val => resolve(val) )}).then( (response) => {console.log("fetchAsyncData code2 ok?", response.ok);return response.json()}).then((data) => {console.log(data);});
}// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");
相关文章:
【js】js 异步机制详解 Generator / Async / Promise
三种语法功能放在一起,是因为他们都有相似特点: 维护某种状态在未来恢复状态并执行 本文重点回答以下几个问题: 为什么 Generator 和 Async 函数的 代码执行流 都可以简化成树形结构?async 函数为什么返回一个 promise…...
【动态规划】【数学】【C++算法】805 数组的均值分割
作者推荐 【动态规划】【数学】【C算法】18赛车 本文涉及知识点 动态规划 数学 805 数组的均值分割 给定你一个整数数组 nums 我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中,使得 A 数组和 B 数组不为空,并且 average(A) average(B)…...
Django笔记(五):模型models
首 Django中的模型对应数据库中的一张表格。 定义模型 player.py from django.db import modelsclass Player(models.Model):idx models.IntegerField(uniqueTrue)def __str__(self):return str(self.id)每个模型需要继承models类,如上Player模型定义了一个整形…...
一个golang小白使用vscode搭建Ununtu20.04下的go开发环境
文章目录 前言搭建go环境下载go安装包解压go压缩包完成安装配置环境变量编写一个helloword程序 安装VSCode插件安装智能提示插件安装go依赖包修改代理并重新安装依赖包 go.mod 和 go.workgo.modgo.work小试一下go.work 总结 前言 先交代一下背景,距离正式接触golan…...
【复现】Hytec Inter HWL 2511 SS路由器RCE漏洞_25
目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议: 五. 搜索语法: 六.免责声明 一.概述 Hytec Inter HWL 2511 SS是日本Hytec Inter 公司的一款工业级 LTE 路由器,可用于远程数据传输,例如收集传…...
Kafka系列(四)
本文接kafka三,代码实践kafkaStream的应用,用来完成流式计算。 kafkastream 关于流式计算也就是实时处理,无时间概念边界的处理一些数据。想要更有性价比地和java程序进行结合,因此了解了kafka。但是本人阅读了kafka地官网&#…...
【Linux学习】进程信号
目录 十七.进程信号 导言 17.1 linux中的信号列表 17.2 标准信号与实时信号 17.3 信号的产生 17.3.1 通过终端按键产生信号 17.3.2 调用系统函数产生信号 17.3.3 软件条件产生信号 17.3.4 硬件异常产生信号 17.3.5 【补充】核心转储 Core Dump 17.4 信号的阻塞 17.4.1 信号相关…...
机器学习没那么难,Azure AutoML帮你简单3步实现自动化模型训练
在Machine Learning 这个领域,通常训练一个业务模型的难点并不在于算法的选择,而在于前期的数据清理和特征工程这些纷繁复杂的工作,训练过程中的问题在于参数的反复迭代优化。 AutoML 是 Azure Databricks 的一项功能,它自动的对…...
数学建模实战Matlab绘图
二维曲线、散点图 绘图命令:plot(x,y,’line specifiers’,’PropertyName’,PropertyValue) 例子:绘图表示年收入与年份的关系 ‘--r*’:--设置线型;r:设置颜色为红色;*节点型号 ‘linewidth’:设置线宽࿱…...
TypeError the JSON object must be str, bytes or bytearray, not ‘list‘
在使用python的jason库时,偶然碰到以下问题 TypeError: the JSON object must be str, bytes or bytearray, not ‘list’ 通过如下代码可复现问题 >>> a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> import json >>> ra json.loads(a) Trac…...
数字IC后端设计实现 | PR工具中到底应该如何控制density和congestion?(ICC2Innovus)
吾爱IC社区星友提问:请教星主和各位大佬,对于一个模块如果不加干预工具会让inst挤成一团,后面eco修时序就没有空间了。如果全都加instPadding会导致面积不够overlap,大家一般怎么处理这种问题? 在数字IC后端设计实现中…...
产品经理与产品运营的区别和联系
一、两者的职责区别 产品经理的目的:是创造有价值的产品 产品运营的目的:是让产品能有效的发挥出它应有的价值 二、两者的工作内容区别产品经理的工作内容 产品的经理的目的是创造有价值的产品,因此产品经理的所有工作都是围绕着…...
CMU15-445-Spring-2023-分布式DBMS初探(lec21-24)
Lecture #21_ Introduction to Distributed Databases Distributed DBMSs 分布式 DBMS 将单个逻辑数据库划分为多个物理资源。应用程序(通常)并不知道数据被分割在不同的硬件上。系统依靠单节点 DBMS 的技术和算法来支持分布式环境中的事务处理和查询执…...
Arch linux 安装
Arch linux 安装 介绍下载制作iSO启动盘安装arch linux设置字体连接互联网 安装过程磁盘分区设置设置镜像源设置引导文件挂载点安装base等基础软件生成fatab文件更改时区更改编码、语言更改编码更改语言 用户管理设置root密码新建普通用户 安装grub启动网络服务/GDM查看系统网络…...
最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作
详情点击链接:最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析,AI画图,图像识别,文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Clau…...
【leetcode】移除元素
大家好,我是苏貝,本篇博客带大家刷题,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 一.暴力求解法二.使用额外数组三.原地修改数组 点击查看题目 一.暴力求解法 若我们不考虑时间复杂度…...
Spring Boot整合Redis的高效数据缓存实践
引言 在现代Web应用开发中,数据缓存是提高系统性能和响应速度的关键。Redis作为一种高性能的缓存和数据存储解决方案,被广泛应用于各种场景。本文将研究如何使用Spring Boot整合Redis,通过这个强大的缓存工具提高应用的性能和可伸缩性。 整合…...
FastApi-参数接收的正确使用(2)
前言 本文是该专栏的第2篇,后面会持续分享FastApi以及项目实战的各种干货知识,值得关注。 本文重点介绍,在使用FastApi使用“参数接收”时遇到的三种类型“路径参数”,“查询参数”,“请求体”的相关问题以及相应的解决方案。 具体详细知识点,跟着笔者直接往下看正文。…...
三、需求规格说明书(软件工程示例)
1.引言 1.1编写目的 1.2项目背景 1.3定义 1.4参考资料 2.任务概述 2.1目标 2.2运行环境 2.3条件与限制 3.数据描述 3.1静态数据 3.2动态数据 3.3数据库介绍 3.4数据词典 3.5数据采集 4.功能需求 …...
Elasticsearch 查询语句概述
目录 1. Match Query 2. Term Query 3. Terms Query 4. Range Query 5. Bool Query 6. Wildcard Query 7. Fuzzy Query 8. Prefix Query 9. Aggregation Query Elasticsearch 是一个基于 Lucene 的搜索引擎,提供了丰富的查询DSL(Domain Specifi…...
Jar Analyzer 污点分析功能详解:如何验证DFS算法推导的方法调用链可行性
Jar Analyzer 污点分析功能详解:如何验证DFS算法推导的方法调用链可行性 【免费下载链接】jar-analyzer Jar Analyzer - 一个 JAR 包 GUI 分析工具,支持 JAR DIFF 分析,方法调用关系搜索,方法调用链 DFS 算法分析,模拟…...
YimMenu完全指南:如何在GTA5中构建你的个人安全增强系统
YimMenu完全指南:如何在GTA5中构建你的个人安全增强系统 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/Yi…...
LTM4644国产替代-ITE4644
ITE4644是四路DC/DC降压模块稳压器,每路可以输出4A。输出可以并联在一个阵列中,最高可达16A的能力。封装内包含开关控制器,功率场效应管,电感器和支持组件。工作在输入电压范围4V~14V或者2.375V~14V(INTVCC/SVIN外置偏置电压)。 I…...
AES128加密实战:用C语言写一个加密函数,并集成到你的STM32项目里
AES128加密实战:用C语言写一个加密函数,并集成到你的STM32项目里 在物联网设备开发中,数据安全传输是至关重要的环节。想象一下,你的STM32设备需要通过无线网络发送传感器数据到云端,如果这些数据以明文传输࿰…...
agent 学习路径解析 学习资源分享
文章目录 先给结论:你接下来不要优先读 GLM-4.5你对 agent 的轻视,有一半对,一半错关于 Claude Code 泄露:你应该学“架构收获”,不要沉迷“源码猎奇”你提到的 learn-claude-code 仓库:值得看,…...
IPXWrapper终极指南:让90年代经典游戏在现代Windows上重生联机对战
IPXWrapper终极指南:让90年代经典游戏在现代Windows上重生联机对战 【免费下载链接】ipxwrapper 项目地址: https://gitcode.com/gh_mirrors/ip/ipxwrapper 对于许多怀旧游戏玩家来说,最大的遗憾莫过于那些经典的《星际争霸》、《帝国时代》、《…...
Excel数据导入实战:为缺失ID列批量生成标准UUID
1. 为什么需要为Excel数据批量生成UUID? 最近在处理一个数据迁移项目时,遇到了一个典型问题:从Navicat导出的Excel表格缺少主键列,导致后续数据导入时频频报错。这种情况在数据迁移、系统对接时特别常见。UUID(通用唯…...
FreeRDP 终极指南:跨平台远程桌面完全解决方案
FreeRDP 终极指南:跨平台远程桌面完全解决方案 【免费下载链接】FreeRDP FreeRDP is a free remote desktop protocol library and clients 项目地址: https://gitcode.com/gh_mirrors/fr/FreeRDP FreeRDP 是一款功能强大的开源远程桌面协议实现库࿰…...
BooruDatasetTagManager AiApiServer深度配置:解决常见模型兼容性问题
BooruDatasetTagManager AiApiServer深度配置:解决常见模型兼容性问题 【免费下载链接】BooruDatasetTagManager 项目地址: https://gitcode.com/gh_mirrors/bo/BooruDatasetTagManager BooruDatasetTagManager是一款功能强大的AI图片标签管理工具ÿ…...
终极Windows和Office激活指南:KMS智能激活工具三步永久激活方案
终极Windows和Office激活指南:KMS智能激活工具三步永久激活方案 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统频繁弹出激活提示而烦恼吗?Office突然变…...
