范式转移:从协程、回调到异步

本文使用 CC BY-NC-ND 4.0 许可。

原文:英文版 | 中文版

协程、回调和异步每一个单独拎出来讲都不难,但它们之间的关系却非常烧脑,迄今为止我还没见过哪篇文章能讲清楚的,所以在这里我尝试用最简单的思路讲解清楚:如何把协程和回调转换到流行的async/await模式,我使用lua 5.1讲解。

Q:为什么不用ecmascript?
A:因为我认为es的那套基于promise的实现把问题复杂化了,而且其中用的是递归而非循环,递归有栈溢出的风险。

首先,什么是协程?正常函数return之后就结束了,但在协程里面还可以使用yield回到调用方,协程并未结束,而是进入睡眠状态,之后调用方可再次唤醒它并从上次yield位置继续往后执行。所以也有人把协程比作线程的,就好像windows 3.1的协作式多任务和windows 95的抢占式多任务。
在这里插入图片描述那么协程和async/await模式有什么关联呢?首先能想到的,await会不会就是yield?没错,在lua里,我直接定义await = coroutine.yield。然后最大的问题是yield是返回到调用方,而await是返回给等号左边。这里就需要范式转移了:resume可以传值给协程,而yield可以传值给调用方,那么调用方把接收到的值原封不动传回协程不就是了?如此循环resume协程直到其dead为止。一句话,把函数包裹为协程,再包裹一层循环,就是异步化。
在这里插入图片描述代码如下,为了简单起见,不包含错误处理:

function async(f)return function(...)local co = coroutine.create(f)local ret = {true, ...}while coroutine.status(co) ~= 'dead' doret = {coroutine.resume(co, unpack(ret, 2))}endreturn unpack(ret, 2)end
end
await = coroutine.yield

Q:就这么简单?
A:对,就这么简单。

比如下面的代码:

local add =async(function(a, b)return a + bend
)
local function mul(a, b)return a * b
end
local main =async(function()local c, d = await(add(1, 2), 3 + 4)local e = await(mul(c, d))print(c, d, e)end
)
main()

Q:为什么main函数可以直接执行?
A:异步化后的本质就是函数,而不是协程,所以可以直接执行。

Q:为什么在await里面可以放普通函数或表达式?
A:await的本质是传参,所以括号内的参数都是必须先计算出结果的,这样就从机制上确保了顺序执行。

Q:那么普通函数和异步化后的函数有什么区别?
A:异步化的魔力在于外部看是函数,但内部是协程环境,是使用await所必须的。

Q:在你的示例里没有异步执行的代码啊?
A:没错,async/await模式只是提供一个能够依照代码的顺序执行的框架,包括协程,本身都没有异步执行的魔法。任何异步的底层一定都是回调函数,只有回调函数才能打乱执行顺序。借助协程可完成转换,以下是范例:

local function callback_example(a, b, cb)cb('Greetings ' .. a .. ' and ' .. b)
end
local function callback_removed(a, b)local co = coroutine.running()local donelocal retcallback_example(a,b,function(...)-- Adone = trueret = {...}if coroutine.status(co) == 'suspended' thencoroutine.resume(co)endend)-- Bif not done thencoroutine.yield()endreturn unpack(ret)
end

callback_removed是转换后的非回调风格的函数。注意:首先它是普通函数,不是协程;其次它内部使用了协程上下文,所以必须在协程内执行。注意看代码里的注释A和B位置,关键点在于,因为callback_example是异步的,所以到底是A处还是B处先执行是未知的,代码使用闭包设置状态变量,在B处做判断如果回调函数尚未执行,则休眠协程,而在回调函数最后则唤醒协程。

callback_removed可直接包裹为协程执行:print(coroutine.resume(coroutine.create(callback_removed), 'tom', 'jerry'))

当然也可以由await调用,因为await本质就是执行该函数:

local main =async(function()print(await(callback_removed('tom', 'jerry')))print(await(callback_removed('foo', 'bar')))end
)
main()

参考:

  • From Javascript Callbacks to Promises to Generators and Coroutines
  • Using coroutines in Neovim Lua

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

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

相关文章

Pytorch学习--神经网络--非线性激活

一、用法 torch.nn.ReLU 图像处理中的应用:在图像处理任务中,ReLU 激活函数能够增强特征提取的能力,使网络更好地捕捉图像的细节和边缘。这是因为 ReLU 对大部分负数响应为零,能在一定程度上减少网络计算量,并对特征…

Objective-C 音频爬虫:实时接收数据的 didReceiveData_ 方法

在互联网技术领域,数据的获取和处理是至关重要的。尤其是对于音频内容的获取,实时性和效率是衡量一个爬虫性能的重要指标。本文将深入探讨在Objective-C中实现音频爬虫时,如何高效地使用didReceiveData:方法来实时接收数据,并通过…

企业自建邮件系统选U-Mail ,功能强大、安全稳定

在现代企业运营中,电子邮件扮演着至关重要的角色,随着企业规模的增长和业务的多样化,传统的租用第三方企业邮箱服务逐渐显现出其局限性。例如,存储空间受限、数据安全风险、缺乏灵活的管理和备份功能,以及无法与其他企…

C++在实际项目中的应用第二节:C++与区块链

第五章:C在实际项目中的应用 第二课:C与区块链 区块链技术因其去中心化、不可篡改和透明性而受到广泛关注。在这门课程中,我们将深入探讨区块链的基本原理、智能合约的开发以及实际应用的案例分析,重点使用 C 作为实现语言&…

雷池社区版中升级雷池遇到问题

关于升级后兼容问题 版本差距过大会可能会发生升级后数据不兼容导致服务器无法起来 跨多个版本(超过1个大版本号)升级做好数据备份,遇到升级失败可尝试重新安装解决 升级提示目录不对 在错误的目录下执行(比如 safeline 的子目…

TCP/IP Attack Lab

网络拓扑: Task 1: SYN Flooding Attack 收到攻击之前,在Victim主机查看网络连接的状态: 在攻击之前使用User1主机(10.9.0.6)访问Victim(10.9.0.5)主机的 Telnet服务: Task 1.1: Launching the Attack Using Python 在Atacker上建立文件attack-1.py…

Jvm中的堆和栈

JVM中的堆和栈分别存放不同的数据类型和内容。 ‌栈(Stack)‌: 存储基本数据类型(如int, char, boolean等)和对象的引用。存储局部变量、方法调用、程序运行状态、方法返回值等。每个线程都有一个独立的线程栈&#…

我为什么投身于青少年AI编程?——打造生态圈(三)

第五部分 青少年AI编程生态圈 一、生态圈 主要涵盖家庭、社区/中小学、高校高职、主管部门。 1、家庭 我们与社区/中小学一道打造让家长满意的模式。 教得好: 费用少: 家门口: 2、社区/中小学 社区党群服务中心和中小学都有大面积科普…

Ubuntu18.04安装vscode1.94.2失败安装vscode1.84.2

系统环境:Ubuntu18.04.6 LTS 自己先去vscode官网下载好最新版本的vscode1.94.2(不下也行,反正最新版也用不了,哈哈) 网址:Visual Studio Code - Code Editing. RedefinedVisual Studio Code is a code ed…

.NET 8 中的 Mini WebApi

介绍 .NET 8 中的极简 API 隆重登场,重新定义了我们构建 Web 服务的方式。如果您想知道极简 API 的工作原理以及它们如何简化您的开发流程,让我们通过一些引人入胜的示例来深入了解一下。 .NET 极简主义的诞生 想想我们曾经不得不为一个简单的 Web 服务…

鸿蒙开发融云demo发送图片消息

鸿蒙开发融云demo发送图片消息 融云鸿蒙版是不带UI的,得自己一步步搭建。 这次讲如何发送图片消息,选择图片,显示图片消息。 还是有点难度的,好好看,好好学。 一、思路: 选择图片用:photoVie…

多条音频按顺序合成 代码实现

根据之前文章后续chatTTS文本转语音 实践相关记录-CSDN博客 直接合成多条音频,顺序会乱,最终合成的效果不显著,改进了合成音频的代码 直接获取文件夹中的所有 WAV 文件,并按文件名排序来合并它们。 wav_files.sort() 默认是按字母…

CMake 生成器表达式介绍

【写在前面】 生成器表达式在构建系统生成期间进行评估&#xff0c;以生成特定于每个构建配置的信息。它们的形式为 $<...>。例如&#xff1a; target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>) 这将扩展为 “/opt/include/GNU”、“/opt…

Java Lock Semaphore 总结

前言 相关系列 《Java & Lock & 目录》&#xff08;持续更新&#xff09;《Java & Lock & Semaphore & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Java & Lock & Semaphore & 总结》&#xff08;学习总结/最新最准…

1.机器人抓取与操作介绍-深蓝学院

介绍 操作任务 操作 • Insertion • Pushing and sliding • 其它操作任务 抓取 • 两指&#xff08;平行夹爪&#xff09;抓取 • 灵巧手抓取 7轴 Franka 对应人的手臂 6轴 UR构型去掉一个自由度 课程大纲 Robotic Manipulation 操作 • Robotic manipulation refers…

STK与MATLAB互联——仿真导航卫星与地面用户间距离和仰角参数

文章目录 构建GPS星座创建单个PRN的GPS卫星创建GPS星座&#xff0c;并为其添加发射机 北斗星座构建搭建低轨铱星星座构建一颗轨道高度为800km/1000km/1200km的低轨卫星构建一颗轨道高度为800km/1000km/1200km的低轨卫星建立地面站&#xff0c;可见性分析确定地面站坐标分析单颗…

大数据之VIP(Virtual IP,虚拟IP)负载均衡

VIP&#xff08;Virtual IP&#xff0c;虚拟IP&#xff09;负载均衡是一种在计算机网络中常用的技术&#xff0c;用于将网络请求流量均匀地分散到多个服务器上&#xff0c;以提高系统的可扩展性、可靠性和性能。以下是对VIP负载均衡的详细解释&#xff1a; 一、VIP负载均衡的基…

挖矿病毒的处理

前阶段生产服务器又中挖矿病毒了&#xff0c;紧急处理了一波 现象 执行 top命令&#xff0c;查看哪里cpu占用较高 CPU 彪满下不来 解决 1、杀掉进程 kill -9 pid 2、但是&#xff0c;过一会又不行了&#xff0c;说明有定时任务在定时执行这个病毒 3、先找到病毒文件&…

java OOP 对象操作

目录 引 对象比较 ”引用比较“与“内容比较” 对象的比较&#xff1a;Comparable接口 泛型化的Comparable接口 使用例子 “”与“equals” 重写equals()的必要性 重写equals方法的要求 重写hashCode( )方法 hashCode() 与 equals() 的关系 重写 hashCode() 的规则…

小柴冲刺软考中级嵌入式系统设计师系列二、嵌入式系统硬件基础知识(6)嵌入式系统总线及通信接口

目录 越努力&#xff0c;越幸运&#xff01; flechazo 小柴冲刺软考中级嵌入式系统设计师系列总目录 一、PCI、PCI-E 等接口基本原理与结构 1、PCI (1)高速性。 (2)即插即用性。 (3)可靠性。 (4)复杂性。 (5)自动配置。 (6)共享中断。 (7)扩展性好。 (8)多路复用。…