您现在的位置是:亿华云 > 域名

面试题:实现小程序平台的并发双工 Rpc 通信

亿华云2025-10-03 20:23:15【域名】6人已围观

简介前几天面试的时候遇到一道面试题,还是挺考验能力的。题目是这样的: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 来标识和关联一组请求响应。

很赞哦!(1)