首页/新闻资讯/正文详情

Embassy 在 WebAssembly 上运行:用 Rust async 编写浏览器端 ticker 应用的完整实战指南

发布时间:2026/9/25 5:48:44 来源:云帆数科 栏目:资讯中心
Embassy 在 WebAssembly 上运行:用 Rust async 编写浏览器端 ticker 应用的完整实战指南
嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载本指南基于当前仓库中的 examples/wasm 示例讲解如何将 Embassy 的异步运行时embassy-executor与时间驱动embassy-time编译为 WASM并在浏览器中运行你将学会使用wasm-pack构建、启动本地 Web 服务器、通过 ES Module 加载产物并理解 Embassy 的 executor 如何通过 Promise 调度到 JavaScript 事件循环、Timer::after_secs又如何借助setTimeout工作。读完本文你可以在浏览器中直接复用熟悉的上层 async API为嵌入式逻辑开发网页端演示或仿真工具。为什么 Embassy 可以跑在浏览器里Embassy 是一个面向嵌入式系统的 Rust async 框架但它的核心executor、同步原语、时间抽象在架构上是平台无关的。仓库中embassy-executor、embassy-time、embassy-sync等 crate 通过 feature 机制选择不同的底层实现在 MCU 上对应 Cortex-M/RISC-V 平台和硬件定时器在浏览器中则对应 WASM 平台和setTimeout。embassy-executor的 Cargo.toml 中定义了多种“flavor”其中之一即为{ name wasm, target wasm32-unknown-unknown, features [platform-wasm, executor-thread] }对应的 feature 定义为## WASM platform. platform-wasm [_platform, dep:wasm-bindgen, dep:js-sys]这意味着启用platform-wasm时executor 通过wasm-bindgen与js-sys对接浏览器运行时。因此同一个#[embassy_executor::task]异步任务既可以在 MCU 上跑也可以不加修改地在浏览器 WASM 中运行——这正是本示例的价值所在。环境准备安装 wasm-pack示例使用官方 CLI 工具wasm-pack进行构建。README 指定的版本为 0.12.1安装命令如下cargo install wasm-pack --version 0.12.1说明wasm-pack负责将 Rust 编译产物打包成可供网页直接引用的pkg目录包含.jsglue 代码与.wasm二进制。请确保你的 Rust 工具链已安装wasm32-unknown-unknown目标可通过rustup target add wasm32-unknown-unknown添加。示例的Cargo.toml中同样声明了构建目标见 package.metadata.embassytarget wasm32-unknown-unknown。示例项目结构一览本示例目录包含四个文件文件作用Cargo.toml定义 crate 类型与 WASM 相关依赖/featuresrc/lib.rsEmbassy 异步任务与入口实现index.html宿主页面以 ES Module 加载编译产物README.md构建与运行说明本文基础由于编译目标是浏览器代码以cdylib形式导出。关键配置如下[lib] crate-type [cdylib] [dependencies] embassy-sync { version 0.8.0, path ../../embassy-sync, features [log] } embassy-executor { version 0.10.0, path ../../embassy-executor, features [platform-wasm, executor-thread, log] } embassy-time { version 0.5.1, path ../../embassy-time, features [log, wasm] }这里每个 feature 都对应一条 WASM 支持链路embassy-executor的platform-wasm选择 platform/wasm.rs 作为 executor 平台实现embassy-executor的executor-thread启用基于线程模型这里即 JS 事件循环驱动的 executor同时该文件开头明确限制了executor-interrupt与platform-wasm不能共存#[cfg(feature executor-interrupt)] compile_error!(executor-interrupt is not supported with platform-wasm.);embassy-time的wasm启用 driver_wasm.rs 时间驱动其 feature 定义为见 embassy-time/Cargo.tomlwasm [dep:wasm-bindgen, dep:js-sys, dep:web-time, tick-hz-1_000_000, dep:embassy-time-queue-utils]wasm-logger、web-sys、log用于在浏览器控制台输出日志并操作 DOM。源码解析一个每秒打印一次的 ticker 任务核心逻辑在 src/lib.rs 中分为任务定义与入口两部分。任务定义操作 DOM 的异步循环#[embassy_executor::task] async fn ticker() { let window web_sys::window().expect(no global window exists); let mut counter 0; loop { let document window.document().expect(should have a document on window); let list document.get_element_by_id(log).expect(should have a log element); let li document.create_element(li).expect(error creating list item element); li.set_text_content(Some(format!(tick {}, counter))); list.append_child(li).expect(error appending list item); log::info!(tick {}, counter); counter 1; Timer::after_secs(1).await; } }#[embassy_executor::task]宏把一个async fn变成可被 spawn 的 Embassy 任务。任务内部通过web-sys操作浏览器 DOM向页面中idlog的ul列表追加li元素同时用log::info!输出到浏览器控制台。关键点在于Timer::after_secs(1).await——这不是忙等而是挂起当前任务并交给时间驱动1 秒后由驱动重新唤醒任务继续执行。入口初始化日志并启动任务#[embassy_executor::main] async fn main(spawner: Spawner) { wasm_logger::init(wasm_logger::Config::default()); spawner.spawn(ticker().unwrap()); }#[embassy_executor::main]在 WASM 平台下实际展开为main_wasm宏见 embassy-executor-macros/src/lib.rs 中pub fn main_wasm的定义它创建 WASM executor 实例把函数体作为初始任务运行并将Spawner传入以便 spawn 更多任务。整个流程非常接近嵌入式写法wasm_logger::init对应 MCU 上的日志初始化spawner.spawn(ticker())对应在嵌入式 main 中启动任务。也就是说这套代码结构完全可以移植到实际的 no_std 目标上。构建wasm-pack build在examples/wasm目录下执行wasm-pack build --target web--target web会生成面向浏览器、以 ES Module 形式导入的产物输出到pkg/子目录其中关键文件包括embassy_wasm_example.jsglue 代码导出默认的初始化函数embassy_wasm_example_bg.wasm编译出的 WebAssembly 二进制embassy_wasm_example.d.tsTypeScript 类型声明可选。构建完成后pkg/目录会出现在examples/wasm下供 index.html 引用。运行启动本地 Web 服务器并在浏览器中观察由于浏览器安全策略不允许从file://直接加载模块化 WASMREADME 建议启动一个本地 HTTP 服务器。在examples/wasm目录下执行python -m http.server然后打开浏览器访问http://127.0.0.1:8000此时应看到页面出现h1Log/h1标题和一个idlog的列表列表每秒新增一个tick 0、tick 1、tick 2……条目同时在浏览器开发者工具Console中可以看到wasm-logger输出的对应log::info!消息。若端口被占用可换用python -m http.server 8080等端口。页面如何加载 WASM 模块index.html 使用 ES Module 语法加载编译产物script typemodule import init from ./pkg/embassy_wasm_example.js; await init(); /script其中init是 wasm-bindgen 生成的初始化函数负责实例化.wasm二进制并执行 Rust 侧入口即main_wasm。页面中的列表容器与代码中get_element_by_id(log)对应h1Log/h1 div ul idlog /ul /div底层原理executor 如何“嫁接”到 JS 事件循环理解这个示例的关键在于 embassy-executor/src/platform/wasm.rs 的实现。WASM 平台没有线程与中断Executor 采用“由 JS 事件循环反复轮询”的模型每个 WASM executor 持有一个WasmContext其中保存了一个js_sys::Promise和一个ClosureExecutor::new()通过Box::leak获得static上下文并把上下文指针交给底层的raw::Executor当任务需要被唤醒时WasmPender::pend被调用它执行promise.then(closure)将一次轮询调度到微任务队列——即“借用”浏览器的 Promise 机制作为调度信号impl crate::pender::Pender for WasmPender { fn pend(context: *mut ()) { let signaler: static WasmContext unsafe { std::mem::transmute(context) }; let _ signaler.promise.then(unsafe { signaler.closure.as_mut() }); } }Executor::start()将闭包写入WasmContext闭包内部调用executor.poll()即每次微任务回调都会推进一次任务轮询。因此Timer::after_secs(1).await的完整链路是任务挂起 → 时间驱动注册一个setTimeout回调见下文→ 1 秒后回调触发 → 唤醒任务 → 通过Promise.then安排poll→ executor 推进ticker任务到下一个tick。时间驱动setTimeout 背后的实现embassy-time在 WASM 下的时间驱动位于 embassy-time/src/driver_wasm.rs。它通过wasm_bindgen直接对接浏览器的定时器 API#[wasm_bindgen] extern C { fn setTimeout(closure: Closuredyn FnMut(), millis: u32) - f64; fn clearTimeout(token: f64); }驱动内部使用web_time::Instant维护单调时钟借助embassy_time_queue_utils::Queue管理定时器队列当某个Timer::after_secs(1)到期时回调触发队列中对应任务再由 pender 唤醒。这也是为什么embassy-time的wasmfeature 需要tick-hz-1_000_000与web-time、js-sys等依赖——时间基准确到微秒级别而真实唤醒仍依赖浏览器定时器。限制与注意事项从 platform/wasm.rs 和仓库配置可以总结出几条使用前提不支持中断驱动 executorexecutor-interruptfeature 在platform-wasm下会触发编译错误只能使用executor-thread模型单线程模型所有任务运行在浏览器主线程的事件循环中任务体内不应包含长时间同步阻塞操作否则会卡住页面与定时器回调需要 Web 环境时间驱动依赖setTimeout等浏览器 API因此该示例面向浏览器而非纯 Node 的裸执行环境如需 Node 环境需另行配置驱动版本锁定README 明确要求wasm-pack版本为 0.12.1安装其他版本时产物结构与初始化方式可能发生变化日志通过wasm-logger输出到 Console若启用logfeature 但未初始化 loggerlog::info!将静默丢弃。小结通过本示例你可以看到Embassy 的抽象层让同一份异步代码横跨嵌入式与浏览器两种截然不同的执行环境。构建命令wasm-pack build --target webpython -m http.server即可在数秒内跑起一个每秒输出 tick 的浏览器应用而其底层executor 借助Promise.then挂入 JS 微任务队列时间驱动借助setTimeout实现延迟唤醒。理解这两条链路的实现细节platform/wasm.rs、driver_wasm.rs你就能在此基础上扩展出更复杂的浏览器端仿真、可视化或协议调试工具并复用embassy-sync、embassy-net等上层组件的能力。赞分享嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载相关推荐FastAPI文档暗黑模式prefers-color-scheme智能主题切换指南 FastAPI文档暗黑模式prefers color scheme智能主题切换指南 FastAPI作为高性能的现代Python Web框架不仅提供了卓后端Web框架API设计Avalonia WebAssembly应用浏览器端运行Avalonia WebAssembly应用浏览器端运行 概述 Avalonia UI作为.NET生态系统中领先的跨平台UI框架现已全面支持WebAssem跨平台桌面应用UI组件fastText 浏览器实战使用 WebAssembly 在浏览器中编译、训练与预测完整指南fastText 浏览器实战使用 WebAssembly 在浏览器中编译、训练与预测完整指南 fastText 是一个用于高效学习词表示word repreNLP机器学习上一篇Snippai常见问题解答新手必看的20个实用技巧下一篇Chrome MCP Server终极后台任务管理指南OffscreenManager与Service Worker深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Orleans 9.x 升级至 10.x 迁移实战指南:包版本、行为变更、序列化状态兼容与并行集群部署
Orleans 9.x 升级至 10.x 迁移实战指南:包版本、行为变更、序列化状态兼容与并行集群部署

后端微服务 【免费下载链接】orleans Cloud Native application framework for .NET 项目地址: https://gitcode.com/gh_mirrors/or/orleans 点击查看 免费下载 本篇技术指南聚焦于将基于 .NET 的云原生框架 Orleans 的应用从 9.x 系列升级到 10.x 系列&#xff0c… · 2026/9/25 5:48:44

用Python自建数据字典工具:从information_schema采集到增量维护
用Python自建数据字典工具:从information_schema采集到增量维护

简介:面向数据库开发者、DBA及需要撰写数据库设计文档的技术人员,这份2.58MB的ZIP压缩包提供了一款可直接运行的数据字典生成工具。工具能自动扫描MySQL、Oracle、SQL Server、PostgreSQL等主流数据库中的表、视图、存储过程、函数与触发器,提… · 2026/9/25 5:48:38

PaddleSpeech 说话人验证工具函数深度解析:get_chunks 音频分段与 Q_from_tokens 先验生成
PaddleSpeech 说话人验证工具函数深度解析:get_chunks 音频分段与 Q_from_tokens 先验生成

人工智能语音音频 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword… · 2026/9/25 5:48:38

Atlas 300V 24G部署YOLOv5全流程实战:驱动、模型转换到多路视频推理
Atlas 300V 24G部署YOLOv5全流程实战:驱动、模型转换到多路视频推理

如果你也在搜“atlas 部署 yolo”的案例,或者被“atlas 300v 24g 是运算加速卡吗”这个问题纠结过,那这篇内容应该能帮你省下不少时间。我手上正好有一张 Atlas 300V 24G,前后折腾了快三个月,从驱动装不上、模型转换报错&#xff… · 2026/9/25 6:55:22

MyBatis核心原理与实战:缓存、分页插件及高频问题排查
MyBatis核心原理与实战:缓存、分页插件及高频问题排查

做Java后端开发的人,几乎都绕不开MyBatis这个名字。从早期的SSH整合到现在的SpringBootMyBatis-Plus,这个半自动ORM框架在国内Java生态里站得非常稳。市面上讲MyBatis使用的文章一抓一大把,但真正讲清楚框架原理和核心特性的内容并不多。很多… · 2026/9/25 6:55:22

gsd-core ADR-457 迁移第三批次:10 个运行时模块转向严格 TypeScript 源码与 Build-at-Publish 构建流程
gsd-core ADR-457 迁移第三批次:10 个运行时模块转向严格 TypeScript 源码与 Build-at-Publish 构建流程

【免费下载链接】gsd-core Git. Ship. Done - Core 项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core 点击查看 免费下载 本文为 gsd-core(GSD Core,一个面向 AI 编码代理的元提示与上下文工程系统)的运行时类型化工程实践… · 2026/9/25 6:55:22

内网流媒体安全:浏览器访问模式的四大优势与落地配置
内网流媒体安全:浏览器访问模式的四大优势与落地配置

折腾内网流媒体的朋友,应该都经历过这种场景:家里NAS跑着Jellyfin或者Emby,手机、电视、电脑上各装一个客户端,家人用起来确实方便。但时间一长,你总会发现不对劲——客户端在后台做了什么你根本不知道,它有… · 2026/9/25 6:55:22

Atlas 300V 24G加速卡部署YOLO实战:从硬件规格到多路视频流优化
Atlas 300V 24G加速卡部署YOLO实战:从硬件规格到多路视频流优化

做边缘AI部署的,今年绕不开的一个词就是Atlas。尤其是Atlas 300V Pro 24G这张卡,在智慧园区、工业质检、安防视频分析这些场景里出镜率非常高。后台经常有人问:Atlas 300V 24G是运算加速卡吗?能跑YOLO吗?单卡到底能扛几… · 2026/9/25 6:55:22

VisiData 剪贴板完全指南:行/单元格/列的内外部复制粘贴与系统剪贴板对接
VisiData 剪贴板完全指南:行/单元格/列的内外部复制粘贴与系统剪贴板对接

数据分析CLI数据可视化 【免费下载链接】visidata A terminal spreadsheet multitool for discovering and arranging data 项目地址: https://gitcode.com/gh_mirrors/vi/visidata 点击查看 免费下载 VisiData 是一款在终端中浏览与整理数据的“电子表格多面手”&… · 2026/9/25 6:55:16

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码