自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試題:實現(xiàn)小程序平臺的并發(fā)雙工 Rpc 通信

開發(fā) 架構(gòu) 移動應(yīng)用
rpc 是遠(yuǎn)程過程調(diào)用,是跨進(jìn)程、跨線程等場景下通信的常見封裝形式。面試題是小程序平臺的雙線程的場景,在一個公共文件里實現(xiàn)雙工的并發(fā)的 rpc 通信。

[[435138]]

前幾天面試的時候遇到一道面試題,還是挺考驗?zāi)芰Φ摹?/p>

題目是這樣的:

rpc 是 remote procedure call,遠(yuǎn)程過程調(diào)用,比如一個進(jìn)程調(diào)用另一個進(jìn)程的某個方法。很多平臺提供的進(jìn)程間通信機(jī)制都封裝成了 rpc 的形式,比如 electron 的 remote 模塊。

小程序是雙線程機(jī)制,兩個線程之間要通信,提供了 postMessage 和 addListener 的 api?,F(xiàn)在要在兩個線程都會引入的 common.js 文件里實現(xiàn) rpc 方法,支持并發(fā)的 rpc 通信。

達(dá)到這樣的使用效果:

  1. const res = await rpc('method', params); 

這道題是有真實應(yīng)用場景的題目,比一些邏輯題和算法題更有意思一些。

實現(xiàn)思路

兩個線程之間是用 postMessage 的 api 來傳遞消息的:

  • 在 rpc 方法里用 postMessage 來傳遞要調(diào)用的方法名和參數(shù)
  • 在 addListener 里收到調(diào)用的時候,調(diào)用 api,然后通過 postMessage 返回結(jié)果或者錯誤

我們先實現(xiàn) rpc 方法,通過 postMessage 傳遞消息,返回一個 promise:

  1. function rpc(method, params) { 
  2.     postMessage(JSON.stringify({ 
  3.         method, 
  4.         params  
  5.     })); 
  6.  
  7.     return new Promise((resolve, reject) => { 
  8.          
  9.     }); 

這個 promise 什么時候 resolve 或者 reject 呢?是在 addListener 收到消息后。那就要先把它存起來,等收到消息再調(diào)用 resolve 或 reject。

為了支持并發(fā)和區(qū)分多個調(diào)用通道,我們加一個 id。

  1. let id = 0; 
  2. function genId() { 
  3.     return ++id; 
  4.  
  5. const channelMap = new Map(); 
  6.  
  7. function rpc(method, params) { 
  8.     const curId = genId(); 
  9.  
  10.     postMessage(JSON.stringify({ 
  11.         id: curId, 
  12.         method, 
  13.         params  
  14.     })); 
  15.  
  16.     return new Promise((resolve, reject) => { 
  17.         channelMap.set(curId, { 
  18.             resolve, 
  19.             reject 
  20.         }); 
  21.     }); 

這樣,就通過 id 來標(biāo)識了每一個遠(yuǎn)程調(diào)用請求和與它關(guān)聯(lián)的 resolve、reject。

然后要處理 addListener,因為是雙工的通信,也就是通信的兩者都會用到這段代碼,所以要區(qū)分一下是請求還是響應(yīng)。

  1. addListener((message) => { 
  2.     const { curId, method, params, res}= JSON.parse(message); 
  3.     if (res) { 
  4.         // 處理響應(yīng) 
  5.     } else { 
  6.         // 處理請求 
  7.     } 
  8. }); 

處理請求就是調(diào)用方法,然后返回結(jié)果或者錯誤:

  1. try { 
  2.     const data = global[method](...params); 
  3.     postMessage({ 
  4.         id 
  5.         res: { 
  6.             data 
  7.         } 
  8.     }); 
  9. } catch(e) { 
  10.     postMessage({ 
  11.         id, 
  12.         res: { 
  13.             error: e.message 
  14.         } 
  15.     }); 

處理響應(yīng)就是拿到并調(diào)用和 id 關(guān)聯(lián)的 resolve 和 reject:

  1. const { resolve, reject  } = channelMap.get(id); 
  2. if(res.data) { 
  3.     resolve(res.data); 
  4. else { 
  5.     reject(res.error); 

全部代碼是這樣的:

  1. let id = 0; 
  2. function genId() { 
  3.     return ++id; 
  4.  
  5. const channelMap = new Map(); 
  6.  
  7. function rpc(method, params) { 
  8.     const curId = genId(); 
  9.  
  10.     postMessage(JSON.stringify({ 
  11.         id: curId, 
  12.         method, 
  13.         params  
  14.     })); 
  15.  
  16.     return new Promise((resolve, reject) => { 
  17.         channelMap.set(curId, { 
  18.             resolve, 
  19.             reject 
  20.         }); 
  21.     }); 
  22.  
  23. addListener((message) => { 
  24.     const { id, method, params, res}= JSON.parse(message); 
  25.     if (res) { 
  26.         const { resolve, reject  } = channelMap.get(id); 
  27.         if(res.data) { 
  28.             resolve(res.data); 
  29.         } else { 
  30.             reject(res.error); 
  31.         } 
  32.     } else { 
  33.         try { 
  34.             const data = global[method](...params); 
  35.             postMessage({ 
  36.                 id 
  37.                 res: { 
  38.                     data 
  39.                 } 
  40.             }); 
  41.         } catch(e) { 
  42.             postMessage({ 
  43.                 id, 
  44.                 res: { 
  45.                     error: e.message 
  46.                 } 
  47.             }); 
  48.         } 
  49.     } 
  50. }); 

我們實現(xiàn)了最開始的需求:

  • 實現(xiàn)了 rpc 方法,返回一個 promise
  • 支持并發(fā)的調(diào)用
  • 兩個線程都引入這個文件,支持雙工的通信

其實主要注意的有兩個點:

  • 要添加一個 id 來關(guān)聯(lián)請求和響應(yīng),這在 socket 通信的時候也經(jīng)常用
  • resolve 和 reject 可以保存下來,后續(xù)再調(diào)用。這在請求取消,比如 axios 的 cancelToken 的實現(xiàn)上也有應(yīng)用

這兩個點的應(yīng)用場景還是比較多的。

總結(jié)

rpc 是遠(yuǎn)程過程調(diào)用,是跨進(jìn)程、跨線程等場景下通信的常見封裝形式。面試題是小程序平臺的雙線程的場景,在一個公共文件里實現(xiàn)雙工的并發(fā)的 rpc 通信。

思路文中已經(jīng)講清楚了,主要要注意的是 promise 的 resolve 和 reject 可以保存下來后續(xù)調(diào)用,通過添加 id 來標(biāo)識和關(guān)聯(lián)一組請求響應(yīng)。

 

責(zé)任編輯:姜華 來源: 神光的編程秘籍
相關(guān)推薦

2009-08-11 15:09:44

一道面試題C#算法

2021-07-04 08:01:30

Synchronize線程安全并發(fā)編程

2023-08-07 14:29:26

模擬電話全雙工通信

2020-06-04 14:40:40

面試題Vue前端

2014-09-19 11:17:48

面試題

2019-06-05 07:47:32

Nginx高并發(fā)多線程

2015-09-10 08:46:15

Java面試題

2020-04-07 14:40:19

Java并發(fā)編程多線程

2020-04-12 22:29:50

程序員MySQL數(shù)據(jù)

2023-11-13 07:37:36

JS面試題線程

2011-03-24 13:27:37

SQL

2022-07-27 08:27:34

Call前端

2012-05-25 10:15:06

Java程序員面試題

2015-09-02 09:32:56

java線程面試

2009-06-06 18:34:05

java面試題

2009-06-06 18:36:02

java面試題

2023-02-17 14:35:15

HashMapNode類型

2014-07-15 11:10:01

面試題面試

2020-09-21 11:10:06

Docker運維面試

2010-11-26 10:53:29

戴爾
點贊
收藏

51CTO技術(shù)棧公眾號