postMessage消息通信Promise化的方法實(shí)現(xiàn)
前言
postMessage
Api 想必大家都不陌生,WebWorker
通信會(huì)用到,iframe
窗口之間通信也會(huì)用到,尤其像一些通過 iframe 嵌入其他項(xiàng)目產(chǎn)品的應(yīng)用,想要實(shí)現(xiàn)實(shí)時(shí)通信,就少不了它。
但是,監(jiān)聽消息基本都是全局注冊(cè)事件來接收對(duì)應(yīng)消息,然后在做分發(fā)處理的。
假如業(yè)務(wù)比較復(fù)雜,流程比較長(zhǎng),消息通信的頻率高,而且通信場(chǎng)景繁瑣,那么通過全局事件處理就顯得不太合適了。
那么問題來了,我們能不能將 postMessage
進(jìn)行一次轉(zhuǎn)化,把他變成類似 Promise
的使用方式,這樣業(yè)務(wù)里使用 postMessage
進(jìn)行通信的時(shí)候,就不需要考慮全局回調(diào)事件了。而且 Promise
和他的語法糖 await
都可以很好的消除回調(diào)地獄的情況。
思考
Promise 化,需要解決那些問題?
- 業(yè)務(wù)中發(fā)起的事件,如何注冊(cè)并消費(fèi)?
- 相同的事件多次發(fā)起,應(yīng)該如何處理響應(yīng)結(jié)果?
我們先來看業(yè)務(wù)中發(fā)起的事件,如何注冊(cè)并消費(fèi)?
舉個(gè)實(shí)際的例子:我們假定有子系統(tǒng)向父級(jí) iframe
獲取 loginToken
的行為。
正常情況我們會(huì)這樣寫:
const messageEventHandler = (event: MessageEvent) => { const data = event.data; // 分發(fā)事件 emit(data?.api, data); } // 接收 window.addEventListener("message", messageEventHandler); // 發(fā)送 window.parent.postMessage({ api: "getLoginToken" }, "*");
這樣寫邏輯有問題嗎?沒有問題!邏輯可以正確的執(zhí)行,事件也成功的被分發(fā)了
通過消息訂閱來接收通知,單發(fā)的時(shí)候看起來沒什么問題,假如
getLoginToken
被多次觸發(fā),或者說,同一個(gè)事件被多次注冊(cè),那么還需要考慮業(yè)務(wù)側(cè)事件執(zhí)行后銷毀,避免重復(fù)觸發(fā)
有沒有辦法可以減輕業(yè)務(wù)側(cè)的心智負(fù)擔(dān)呢?
辦法總比困難多嘛~
我們可以使用一個(gè)全局變量
PostMessageCallBackMap
來存放注冊(cè)的事件及回調(diào),Promise
中的resolve
正好有閱后即焚的特性,那么我們是不是可以考慮把創(chuàng)建出來的resolve
作為callback
放到Map
中呢?
想到就立即行動(dòng)!
實(shí)現(xiàn)
改造后的代碼如下:
export const PostMessageCallBackMap = new Map(); const messageEventHandler = (event: MessageEvent) => { // 事件分發(fā) const data = event.data; let eventKey = data?.api; if (PostMessageCallBackMap.has(eventKey)) { PostMessageCallBackMap.get(eventKey)(data); } }; // 接收 window.addEventListener("message", messageEventHandler);
接收我們寫好了,發(fā)送這塊怎么實(shí)現(xiàn)呢?
// 發(fā)送 function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> { return new Promise((resolve, reject) => { if (window.parent) { window.parent.postMessage(param, "*"); // 將當(dāng)前的 resolve 添加到 Map 中,等待返回事件觸發(fā) let eventKey = param.api; PostMessageCallBackMap.set(eventKey, resolve); } }); } // 這樣封裝一下,業(yè)務(wù)中使用就十分方便了 const { token } = await sendMessage({ api: "getLoginToken" }) console.log(token);
這樣看起來舒服了很多,業(yè)務(wù)中應(yīng)用起來也順手了很多,完結(jié)撒花~
等等!
還有個(gè)問題吶。
相同的事件多次發(fā)起,應(yīng)該如何處理響應(yīng)結(jié)果?
陷入沉思
emm,按上面的寫法,多次發(fā)送 getLoginToken
事件,Map 中的 Key 是唯一的,之前的事件會(huì)被覆蓋掉,再加上異步事件返回時(shí)間不確定的話,完蛋了??!
擺爛!
注釋加上,不要調(diào)多次!
結(jié)束!
測(cè)試:哦?是嗎?(狂點(diǎn)、狂點(diǎn)、狂點(diǎn))
好啦好啦,雖然我們可以通過防抖來避免測(cè)試瘋狂轟炸,但是某些場(chǎng)景下真的會(huì)有連續(xù)觸發(fā) api 的情況,那我們?cè)趺唇鉀Q呢?
每一個(gè)回調(diào)給他一個(gè)唯一id,然后通過事件id來進(jìn)行匹配,不管事件執(zhí)行的時(shí)間長(zhǎng)短,通過事件id總可以找到另一半。
唯一id,最簡(jiǎn)單的我們就用時(shí)間戳吧。
改造!改造!改造!
export const PostMessageCallBackMap = new Map(); // 發(fā)送 function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> { return new Promise((resolve, reject) => { if (window.parent) { param.timestamp = Date.now(); window.parent.postMessage(param, "*"); let eventKey = param.api + param.timestamp; PostMessageCallBackMap.set(eventKey, resolve); } }); } // 接收 const messageEventHandler = (event: MessageEvent) => { // 事件分發(fā) const data = event.data; let eventKey = data?.api; // timestamp 來保證對(duì)應(yīng)關(guān)系 eventKey = data?.api + (data?.timestamp || ""); if (PostMessageCallBackMap.has(eventKey)) { PostMessageCallBackMap.get(eventKey)(data); // 由于事件id的存在,Map會(huì)越來越大,清除字典防止內(nèi)存泄漏 setTimeout(() => { PostMessageCallBackMap.delete(eventKey); }, 0); } }; window.addEventListener("message", messageEventHandler);
總結(jié)
通過這樣一番分析改造,終于讓難以控制的全局注冊(cè)事件 postMessage
變成了可以鏈?zhǔn)秸{(diào)用的 Promise
,又可以愉快的寫業(yè)務(wù)代碼啦~
演示地址:https://ztstory.github.io/vue-composition-demo/#/PostMessagePromise
源碼地址:https://github.com/ZTStory/vue-composition-demo
到此這篇關(guān)于postMessage消息通信Promise化的方法實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)postMessage消息Promise化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過javascript的匿名函數(shù)來分析幾段簡(jiǎn)單有趣的代碼
想起自己很久以前學(xué)習(xí)javascript的經(jīng)歷,也曾經(jīng)碰到過幾個(gè)由匿名函數(shù)造成的困擾(其中一個(gè)就是由閉包引起的),下面就整理幾段簡(jiǎn)單代碼討論一下,讓我們大家一起進(jìn)步。2010-06-06微信小程序?qū)崿F(xiàn)頁面浮動(dòng)導(dǎo)航
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)頁面浮動(dòng)導(dǎo)航,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01echarts多條折線圖動(dòng)態(tài)分層的實(shí)現(xiàn)方法
這篇文章主要介紹了echarts多條折線圖動(dòng)態(tài)分層的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05微信小程序?qū)崿F(xiàn)聊天對(duì)話(文本、圖片)功能
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)聊天對(duì)話功能,可以發(fā)送文本、圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07