摘要:結(jié)合與的發(fā)布訂閱模式實(shí)踐本文初衷最近恰好在公司做了一個(gè)聊天室系統(tǒng),所以在系統(tǒng)中做了一下對(duì)進(jìn)行的化改造,所以想寫篇文章總結(jié)一下,如果大家有什么更好的方法或者心得感悟,歡迎交流技術(shù)??紤]到對(duì)并沒什么本質(zhì)影響,所以本文就不涉及了業(yè)務(wù)場(chǎng)景基于的聊天
結(jié)合promise與websocket的發(fā)布/訂閱模式實(shí)踐 本文初衷
最近恰好在公司做了一個(gè)聊天室系統(tǒng),所以在系統(tǒng)中做了一下對(duì)websocket進(jìn)行的promise化改造,所以想寫篇文章總結(jié)一下,如果大家有什么更好的方法或者心得感悟,歡迎交流
技術(shù)棧dva + protobuf
考慮到protobuf對(duì)websocket并沒什么本質(zhì)影響,所以本文就不涉及了
基于websocket的聊天室系統(tǒng)
開發(fā)痛點(diǎn)可能存在按順序觸發(fā)的請(qǐng)求
eg. 刪除req---確認(rèn)刪除rsp---刷新頁(yè)面req---刷新頁(yè)面rsp
但由于并非所有的刪除操作后都會(huì)刷新頁(yè)面,所以考慮是不是可以使用發(fā)布訂閱模式來模擬類似promise的流式操作
存在同一類型請(qǐng)求短時(shí)間內(nèi)多次觸發(fā)時(shí),如何尋找每一條回復(fù)信息的發(fā)射源,考慮可以使用promise池+唯一識(shí)別碼token來實(shí)現(xiàn)
由于dva的異步操作是基于redux-saga的,所以如果可以用promise完成與websocket的互動(dòng),那么對(duì)于effects中使用yield控制異步流程,也會(huì)是一個(gè)很好的體驗(yàn)
實(shí)現(xiàn)原理首先,這一套系統(tǒng)的一切前提是請(qǐng)求的唯一標(biāo)識(shí)符token,前端發(fā)送給服務(wù)器之后,服務(wù)器必須要把這個(gè)token跟數(shù)據(jù)放在一起發(fā)回來才行
本系統(tǒng)的實(shí)現(xiàn)原理是
對(duì)websocket的send方法進(jìn)行封裝 發(fā)送階段 1. send執(zhí)行時(shí),先生成一個(gè)promise,及其唯一token 2. 將promise的resolve, reject,token,及其他需要的信息放入一個(gè)對(duì)象,然后推入一個(gè)promise池中 3. 執(zhí)行websocket的send方法 4. return 這個(gè)promise 接收階段 1. 收到回復(fù)消息時(shí),先從promise池中對(duì)token進(jìn)行匹配 2. 根據(jù)回復(fù)的狀態(tài)決定執(zhí)行resolve或reject 3. 其他需要的操作實(shí)現(xiàn)代碼
// 每一個(gè)實(shí)例都只能open一條socket線路,用鎖機(jī)制防止重復(fù)open // 本例中不使用心跳檢測(cè),為了方便,只要close是非主動(dòng)觸發(fā)且前端能捕捉到的(如瀏覽器主動(dòng)斷開,服務(wù)器主動(dòng)斷開),都會(huì)進(jìn)行自動(dòng)重連 export class MyWebSocket { constructor(url) { this.url = url; // close來源判斷及后續(xù)操作 this.closeConfig = { resolve: null, closing: false } // promise池 this.promisePool = []; } tokenCheck(req, rsp) { // 此處根據(jù)自己的數(shù)據(jù)結(jié)構(gòu)進(jìn)行tokenCheck的判斷,返回一個(gè)boolean } open() { return new Promise((resolve, reject) => { if (typeof this._websocket === "undefined") { this._websocket = new WebSocket(this.url); this._websocket.open = (e) => { resolve({e, ws: this}); }; this._websocket.onerror = (e) => { reject(e); } } this._websocket.onclose = (e) => { // 非主動(dòng)close if (!this.closeConfig.closing) { console.log("reconnect"); // 對(duì)應(yīng)的重連操作 } // 若手動(dòng)close,恢復(fù)初始狀態(tài) this.closeConfig.closing = false; } this._websocket.onmessage = (e) => { this.promisePool = this.promisePool.filter((item) => { if (this.tokenCheck(req, rsp) { req.resolve(rsp); return false; } return true; }) }; }); } close() { this.closeConfig.closing = true; this._websocket.close(); } // token包含在content中 send(name, content) { return new Promise((resolve, reject) => { this.promisePool.push({ content, resolve, reject, name }); this._websocket.send({name, content}); }); }
大概流程就是這樣,具體的樣例如下
*test () { const ws = new MyWebSocket("www.mywebsocket.com"); yield ws.open(); yield ws.send(.....).then(()=>{...}); yield ws.send(.....).then(()=>{...}); }
本文呢大概就是這么多了,如果有什么錯(cuò)誤或者遺漏的地方還請(qǐng)大家多多指教
v0.0.2采取了評(píng)論大佬的建議,將promise池從數(shù)組改為對(duì)象,直接將token做為key,查詢起來也非常方便
export class MyWebSocket { constructor(url) { this.url = url; // close來源判斷及后續(xù)操作 this.closeConfig = { resolve: null, closing: false } // promise池 this.promisePool = {}; } tokenCheck(req, rsp) { // 此處根據(jù)自己的數(shù)據(jù)結(jié)構(gòu)進(jìn)行tokenCheck的判斷,返回一個(gè)boolean } open() { return new Promise((resolve, reject) => { if (typeof this._websocket === "undefined") { this._websocket = new WebSocket(this.url); this._websocket.open = (e) => { resolve({e, ws: this}); }; this._websocket.onerror = (e) => { reject(e); } } this._websocket.onclose = (e) => { // 非主動(dòng)close if (!this.closeConfig.closing) { console.log("reconnect"); // 對(duì)應(yīng)的重連操作 } // 若手動(dòng)close,恢復(fù)初始狀態(tài) this.closeConfig.closing = false; } this._websocket.onmessage = (e) => { const key = e.content.token; const req = this.promisePool[key] req.resolve(e); delete this.promisePool[key]; }; }); } close() { this.closeConfig.closing = true; this._websocket.close(); } // token包含在content中 send(name, content) { return new Promise((resolve, reject) => { this.promisePool[content.token] = { content, resolve, reject, name }; this._websocket.send({name, content}); }); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94995.html
摘要:原理對(duì)處理函數(shù)進(jìn)行延時(shí)操作,若設(shè)定的延時(shí)到來之前,再次觸發(fā)事件,則清除上一次的延時(shí)操作定時(shí)器,重新定時(shí)。在目標(biāo)發(fā)出內(nèi)容改變的事件后,直接接收事件并作出響應(yīng)。首先是目標(biāo)的構(gòu)造函數(shù),他有個(gè)數(shù)組,用于添加觀察者。 關(guān)于排序 js中sort函數(shù)的底層實(shí)現(xiàn)機(jī)制? js中sort內(nèi)置多種排序算法,是根據(jù)要排序數(shù)的亂序程度來決定使用哪一種排序方法。V8 引擎 sort 函數(shù)只給出了兩種排序 Inse...
摘要:我在工程實(shí)踐中直接使用類作為對(duì)應(yīng)實(shí)體類的。因此我的結(jié)論是,此庫(kù)并不適用于我的工程實(shí)踐。工程實(shí)踐中對(duì)其應(yīng)用方式的考慮在的官方教程中建議針對(duì)每請(qǐng)求創(chuàng)建新的實(shí)例,查詢請(qǐng)求結(jié)束則實(shí)例們的生命周期結(jié)束。 因?yàn)樽约簩戇^基于react的前端應(yīng)用,因此一看到GraphQL就被深深吸引,真是直擊痛點(diǎn)??!服務(wù)端開發(fā)一直是基于java, Spring的,因此開始研究如何在現(xiàn)有工程框架下加入graphql的支...
摘要:站點(diǎn)接收到請(qǐng)求后,對(duì)請(qǐng)求進(jìn)行驗(yàn)證,并確認(rèn)是受害者的憑證,誤以為是無辜的受害者發(fā)送的請(qǐng)求。函數(shù)內(nèi)部語(yǔ)句返回的值,會(huì)成為方法回調(diào)函數(shù)的參數(shù)。 記錄我最近面試缺漏的知識(shí)點(diǎn) css 1.bootstrap如何實(shí)現(xiàn)手機(jī)PC端自適應(yīng) 媒體查詢 2.flex布局 父容器:(記得常用屬性) display:flex flex-direction: row | row-reverse | column ...
摘要:該區(qū)域代表可以被所控制的畫布。那么現(xiàn)在第二個(gè)問題,識(shí)別該文檔,這或許不是大部分用戶的需求,但小部分用戶并不意味著人數(shù)少。因此一個(gè)基于的請(qǐng)求于標(biāo)準(zhǔn)內(nèi)提出。 前言 作為程序員,技術(shù)的落實(shí)與鞏固是必要的,因此想到寫個(gè)系列,名為 why what or how 每篇文章試圖解釋清楚一個(gè)問題。 這次的 why what or how 主題:現(xiàn)在幾乎所有人都知道了 HTML5 ,那么 H5 到底相...
閱讀 1535·2021-11-22 09:34
閱讀 3332·2021-09-29 09:35
閱讀 578·2021-09-04 16:40
閱讀 2922·2019-08-30 15:53
閱讀 2596·2019-08-30 15:44
閱讀 2593·2019-08-30 14:10
閱讀 1337·2019-08-29 18:43
閱讀 2219·2019-08-29 13:26