[[435138]]
前几天面试的面试时候遇到一道面试题,还是题实通信挺考验能力的。
题目是程序这样的:
rpc 是 remote procedure call,远程过程调用,平台比如一个进程调用另一个进程的发双某个方法。很多平台提供的面试进程间通信机制都封装成了 rpc 的形式,比如 electron 的题实通信 remote 模块。
小程序是程序双线程机制,两个线程之间要通信,平台提供了 postMessage 和 addListener 的发双 api。现在要在两个线程都会引入的 common.js 文件里实现 rpc 方法,支持并发的 rpc 通信。
达到这样的使用效果:
- const res = await rpc('method', params);
这道题是有真实应用场景的题目,比一些逻辑题和算法题更有意思一些。
两个线程之间是用 postMessage 的 api 来传递消息的:
我们先实现 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 是远程过程调用,是跨进程、跨线程等场景下通信的常见封装形式。面试题是小程序平台的双线程的场景,在一个公共文件里实现双工的并发的 rpc 通信。
思路文中已经讲清楚了,主要要注意的是 promise 的 resolve 和 reject 可以保存下来后续调用,通过添加 id 来标识和关联一组请求响应。
责任编辑:姜华 来源: 神光的编程秘籍 面试题小程序Rpc 通信
(责任编辑:焦点)
合丰集团(02320.HK)发布公告:年度公司拥有人应占亏损1.72亿港元
第二代骁龙7+发布 Redmi Note12 Turbo全球首发
华润医药(03320.HK):东阿阿胶年度实现净利4328.93万元 基本每股收益0.07元