momocow / node-cq-websocket

A Node SDK for developing QQ chatbots based on WebSocket, which is depending on CoolQ and CQHTTP API plugin.

Home Page:https://cq-websocket.js.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use WeakMap for mappings of once listeners and their wrappers

momocow opened this issue · comments

在現在的做法中
一次性的監聽器實際上是先經過一層封裝 (為了自動移除)
而後才註冊到 event map 中

因此透過一個 Map 對象負責配對原先的監聽器到封裝後的方法
用以輔助 #off(event, listener) 找到正確的封裝方法
並將其移除。

若用戶未主動呼叫#off()方法, 或者並未有呼叫#off()方法的需求,
內存中仍有這個 Map 對象配對, 增加 memory leak 風險。

因此改用 WeakMap 可提升這部分的性能,
在現有的慣例中,
所有需要呼叫#off()方法的用戶自然會握有監聽器的參照,
沒握有參照的就是不需要呼叫#off()方法。

閉包參照示意圖

這不是流程圖, 這是當用戶呼叫 #once(event, listener) 時閉包參照的示意圖

藍色兩條是 #once() 中的局部變數, #once() 結束後會消失

我們給 onceWrapper 這個變數指定一個方法(圖中的 once listener )作為值,
這個方法做兩件事情:

  1. 執行 listener, 所以它擁有一條通往 listener 的參照 (綠色)
  2. 執行完畢, 將整個 onceWrapper 自 event map 中移除, 因此它也擁有自己的參照 (橘色)

#once() 結束前會將 onceWrapper 註冊到 event map 中,
因此在 event map 留下紅色參照。

今天有兩個狀況

  1. 用戶未保留 listener 參照, 任 listener 執行一次後自己消失
  2. listener 尚未執行, 便被用戶呼叫 #off(event, listener) 移除

前者 listener 可以順利被執行的關鍵是紅色參照, 唯一使 listener 連結外部的參照
紅色參照在執行完畢後, 會自行呼叫 #off() 斷開這條參照

後者關鍵除了紅色參照, 尚有用戶自行保有的參照
當用戶呼叫#off() 斷開紅色參照,
只須讓握有的參照也失效即可 (如將該方法變量指向null)