連繫promise與websocket的宣布/定閱形式實踐
本文初志
近來恰幸虧公司做了一個聊天室體系,所以在體系中做了一下對websocket舉行的promise化革新,所以想寫篇文章總結一下,假如人人有什麼更好的要領或許心得感悟,迎接交換
手藝棧
dva + protobuf
斟酌到protobuf對websocket並沒什麼實質影響,所以本文就不觸及了
營業場景
基於websocket的聊天室體系
開發痛點
- 能夠存在按遞次觸發的要求
eg. 刪除req—確認刪除rsp—革新頁面req—革新頁面rsp
但因為並不是統統的刪除操縱后都邑革新頁面,所以斟酌是否是能夠運用宣布定閱形式來模仿相似promise的流式操縱 - 存在統一範例要求短時間內屢次觸發時,怎樣尋覓每一條復興信息的發射源,斟酌能夠運用promise池+唯一識別碼token來完成
- 因為dva的異步操縱是基於redux-saga的,所以假如能夠用promise完成與websocket的互動,那末關於effects中運用yield掌握異步流程,也會是一個很好的體驗
完成道理
起首,這一套體系的統統條件是要求的唯一標識符token,前端發送給服務器以後,服務器必須要把這個token跟數據放在一同發回來才行
本體系的完成道理是
對websocket的send要領舉行封裝
發送階段
1. send實行時,先天生一個promise,及其唯一token
2. 將promise的resolve, reject,token,及其他須要的信息放入一個對象,然後推入一個promise池中
3. 實行websocket的send要領
4. return 這個promise
吸收階段
1. 收到復興音訊時,先從promise池中對token舉行婚配
2. 依據復興的狀況決議實行resolve或reject
3. 其他須要的操縱
完成代碼
// 每個實例都只能open一條socket線路,用鎖機制防備反覆open
// 本例中不運用心跳檢測,為了輕易,只需close黑白主動觸發且前端能捕捉到的(如瀏覽器主動斷開,服務器主動斷開),都邑舉行自動重連
export class MyWebSocket {
constructor(url) {
this.url = url;
// close泉源推斷及後續操縱
this.closeConfig = {
resolve: null,
closing: false
}
// promise池
this.promisePool = [];
}
tokenCheck(req, rsp) {
// 此處依據本身的數據結構舉行tokenCheck的推斷,返回一個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) => {
// 非主動close
if (!this.closeConfig.closing) {
console.log('reconnect');
// 對應的重連操縱
}
// 若手動close,恢復初始狀況
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(()=>{...});
}
本文呢也許就是這麼多了,假如有什麼毛病或許脫漏的處所還請人人多多指教
v0.0.2
採取了批評大佬的發起,將promise池從數組改成對象,直接將token做為key,查詢起來也異常輕易
export class MyWebSocket {
constructor(url) {
this.url = url;
// close泉源推斷及後續操縱
this.closeConfig = {
resolve: null,
closing: false
}
// promise池
this.promisePool = {};
}
tokenCheck(req, rsp) {
// 此處依據本身的數據結構舉行tokenCheck的推斷,返回一個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) => {
// 非主動close
if (!this.closeConfig.closing) {
console.log('reconnect');
// 對應的重連操縱
}
// 若手動close,恢復初始狀況
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});
});
}