您现在的位置是:亿华云 > 数据库
面试题:实现小程序平台的并发双工 Rpc 通信
亿华云2025-10-04 00:52:53【数据库】4人已围观
简介前几天面试的时候遇到一道面试题,还是挺考验能力的。题目是这样的:rpc 是 remote procedure call,远程过程调用,比如一个进程调用另一个进程的某个方法。很多平台提供的进程间通信机制
前几天面试的面试时候遇到一道面试题,还是题实通信挺考验能力的。
题目是程序这样的:
rpc 是 remote procedure call,远程过程调用,平台比如一个进程调用另一个进程的发双某个方法。很多平台提供的面试进程间通信机制都封装成了 rpc 的形式,比如 electron 的题实通信 remote 模块。
小程序是程序双线程机制,两个线程之间要通信,平台提供了 postMessage 和 addListener 的发双 api。现在要在两个线程都会引入的面试 common.js 文件里实现 rpc 方法,支持并发的题实通信 rpc 通信。
达到这样的程序使用效果:
const res = await rpc(method, params);这道题是有真实应用场景的题目,比一些逻辑题和算法题更有意思一些。平台
实现思路
两个线程之间是发双用 postMessage 的 api 来传递消息的源码库:
在 rpc 方法里用 postMessage 来传递要调用的方法名和参数 在 addListener 里收到调用的时候,调用 api,然后通过 postMessage 返回结果或者错误我们先实现 rpc 方法,通过 postMessage 传递消息,返回一个 promise:
function rpc(method, params) { postMessage(JSON.stringify({ method, params })); return new Promise((resolve, reject) => { }); }这个 promise 什么时候 resolve 或者 reject 呢?是在 addListener 收到消息后。那就要先把它存起来,等收到消息再调用 resolve 或 reject。
为了支持并发和区分多个调用通道,我们加一个 id。
let id = 0; function genId() { return ++id; } const channelMap = new Map(); function rpc(method, params) { const curId = genId(); postMessage(JSON.stringify({ id: curId, method, params })); return new Promise((resolve, reject) => { channelMap.set(curId, { resolve, reject }); }); }这样,就通过 id 来标识了每一个远程调用请求和与它关联的 resolve、reject。
然后要处理 addListener,因为是双工的通信,也就是通信的两者都会用到这段代码,所以要区分一下是请求还是响应。
addListener((message) => { const { curId, method, params, res}= JSON.parse(message); if (res) { // 处理响应 } else { // 处理请求 } });处理请求就是调用方法,然后返回结果或者错误:
try { const data = global[method](...params); postMessage({ id res: { data } }); } catch(e) { postMessage({ id, res: { error: e.message } }); }处理响应就是拿到并调用和 id 关联的 resolve 和 reject:
const { resolve, reject } = channelMap.get(id); if(res.data) { resolve(res.data); } else { reject(res.error); }全部代码是这样的:
let id = 0; function genId() { return ++id; } const channelMap = new Map(); function rpc(method, params) { const curId = genId(); postMessage(JSON.stringify({ id: curId, method, params })); return new Promise((resolve, reject) => { channelMap.set(curId, { resolve, reject }); }); } addListener((message) => { const { id, method, params, res}= JSON.parse(message); if (res) { const { resolve, reject } = channelMap.get(id); if(res.data) { resolve(res.data); } else { reject(res.error); } } else { try { const data = global[method](...params); postMessage({ id res: { data } }); } catch(e) { postMessage({ id, res: { error: e.message } }); } } });我们实现了最开始的源码下载需求:
实现了 rpc 方法,返回一个 promise 支持并发的调用 两个线程都引入这个文件,支持双工的通信其实主要注意的有两个点:
要添加一个 id 来关联请求和响应,这在 socket 通信的时候也经常用 resolve 和 reject 可以保存下来,后续再调用。这在请求取消,比如 axios 的 cancelToken 的实现上也有应用这两个点的应用场景还是比较多的。
总结
rpc 是远程过程调用,是跨进程、跨线程等场景下通信的常见封装形式。面试题是小程序平台的双线程的场景,在一个公共文件里实现双工的并发的 rpc 通信。
思路文中已经讲清楚了,主要要注意的是 promise 的亿华云计算 resolve 和 reject 可以保存下来后续调用,通过添加 id 来标识和关联一组请求响应。
很赞哦!(6579)
相关文章
- 2、根据用户基础选择访问提供程序。由于互联问题的存在,接入商的选择也非常重要,如果用户群主要在联通,尽量选择联通接入较好的接入商,如果用户群主要在电信,那么选择电信接入较好的接入商。如果用户组位于国家/地区,则选择更好的访问提供程序进行交互。
- 3大利器推荐,帮你写出规范漂亮的Python代码
- 你与数据科学家只差这 26 条 Python 技巧
- 全面分析前端的网络请求方式
- 只要我们做的是从目前的市场情况选择域名,从简单易记,从个性特征上,我们就可以找到一个好域名进行注册。域名注册进行域名记录和解析以及绑定网站后,客户可以通过URL登录您的网站。
- Web性能优化:理解及使用JavaScript缓存
- 1月份GitHub上最热门的Java开源项目
- 软件测试入门指南:周期、模型和文档化
- 4、企业无形资产:通用网站已成为企业网络知识产权的重要组成部分,属于企业的无形资产,也有助于提升企业的品牌形象和技术领先形象。它是企业品牌资产不可或缺的一部分。
- 中国程序员仅凭借一段劳动法则霸榜GitHub,每个人都值得反思
热门文章
站长推荐
其次,一般域名注册有一个获取密码的按钮,域名注册商点击后会向您发送密码。在得到域名注册商发送的密码后,将其传输到域名服务提供商网站,然后输入密码,此时域名呈现申请状态。提交申请后,原注册人通常会向您发送一封电子邮件,询问您是否同意转让。此时,您只需点击同意转移按钮,域名注册商就可以成功转移。
从10秒到2秒!ElasticSearch性能调优实践
2019年前端工程师自检清单与思考
一路打怪升级,360推荐系统架构演进
当投资者经过第二阶段的认真学习之后又充满了信心,认为自己可以在市场上叱咤风云地大干一场了。但没想到“看花容易绣花难”,由于对理论知识不会灵活运用.从而失去灵活应变的本能,就经常会出现小赢大亏的局面,结果往往仍以失败告终。这使投资者很是困惑和痛苦,不知该如何办,甚至开始怀疑这个市场是不是不适合自己。在这种情况下,有的人选择了放弃,但有的意志坚定者则决定做最后的尝试。
Spring Cloud Stream 使用延迟消息实现定时任务(RabbitMQ)
状态机在马蜂窝机票订单交易系统中的应用与优化实践
Lambda架构已死,基于IOTA模型的“秒算平台”架构实践