eventBus.js

7.22 KB
12/10/2025 05:04
JS
eventBus.js
/**
 * EventBus - ระบบจัดการเหตุการณ์
 * ใช้สำหรับเชื่อมโยงการสื่อสารระหว่างโมดูลต่างๆ
 */
(function(global) {
  'use strict';

  /**
   * คลาส EventBus
   * จัดการการส่งและรับเหตุการณ์ระหว่างโมดูล
   */
  class EventBus {
    constructor() {
      this.events = {};
      this.history = [];
      this.maxHistorySize = 100;
    }

    /**
     * ลงทะเบียนฟังเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์
     * @param {function} callback - ฟังก์ชันที่จะเรียกเมื่อเหตุการณ์เกิดขึ้น
     * @param {object} context - บริบทสำหรับฟังก์ชัน (optional)
     * @returns {object} ออบเจกต์สำหรับยกเลิกการลงทะเบียน
     */
    on(event, callback, context = null) {
      if (!this.events[event]) {
        this.events[event] = [];
      }

      const listener = {
        callback,
        context,
        id: this.generateId()
      };

      this.events[event].push(listener);

      // ส่งคืนออบเจกต์สำหรับยกเลิกการลงทะเบียน
      return {
        off: () => {
          this.off(event, callback, context);
        }
      };
    }

    /**
     * ลงทะเบียนฟังเหตุการณ์ (ครั้งเดียว)
     * @param {string} event - ชื่อเหตุการณ์
     * @param {function} callback - ฟังก์ชันที่จะเรียกเมื่อเหตุการณ์เกิดขึ้น
     * @param {object} context - บริบทสำหรับฟังก์ชัน (optional)
     * @returns {object} ออบเจกต์สำหรับยกเลิกการลงทะเบียน
     */
    once(event, callback, context = null) {
      const onceCallback = (data) => {
        this.off(event, onceCallback, context);
        callback.call(context, data);
      };

      return this.on(event, onceCallback, context);
    }

    /**
     * ยกเลิกการลงทะเบียนฟังเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์
     * @param {function} callback - ฟังก์ชันที่จะยกเลิก
     * @param {object} context - บริบทสำหรับฟังก์ชัน (optional)
     */
    off(event, callback, context = null) {
      if (!this.events[event]) return;

      this.events[event] = this.events[event].filter(listener => {
        return listener.callback !== callback || listener.context !== context;
      });

      // ลบเหตุการณ์ถ้าไม่มีผู้ฟัง
      if (this.events[event].length === 0) {
        delete this.events[event];
      }
    }

    /**
     * ส่งเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์
     * @param {*} data - ข้อมูลที่จะส่งไปกับเหตุการณ์
     * @param {boolean} async - ส่งแบบอะซิงโครนัสหรือไม่ (default: true)
     */
    emit(event, data = {}, async = true) {
      // บันทึกประวัติเหตุการณ์
      this.recordEvent(event, data);

      if (!this.events[event]) return;

      const listeners = [...this.events[event]]; // สำเนาเพื่อป้องกันการแก้ไขระหว่างการวนซ้ำ

      if (async) {
        // ส่งแบบอะซิงโครนัส
        setTimeout(() => {
          listeners.forEach(listener => {
            try {
              listener.callback.call(listener.context, data);
            } catch (error) {
              console.error(`Error in event listener for '${event}':`, error);
            }
          });
        }, 0);
      } else {
        // ส่งแบบซิงโครนัส
        listeners.forEach(listener => {
          try {
            listener.callback.call(listener.context, data);
          } catch (error) {
            console.error(`Error in event listener for '${event}':`, error);
          }
        });
      }
    }

    /**
     * บันทึกประวัติเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์
     * @param {*} data - ข้อมูลที่ส่งไปกับเหตุการณ์
     */
    recordEvent(event, data) {
      this.history.push({
        event,
        data,
        timestamp: new Date()
      });

      // จำกัดขนาดประวัติ
      if (this.history.length > this.maxHistorySize) {
        this.history.shift();
      }
    }

    /**
     * รับประวัติเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์ (optional)
     * @returns {array} ประวัติเหตุการณ์
     */
    getHistory(event = null) {
      if (event) {
        return this.history.filter(item => item.event === event);
      }
      return [...this.history]; // ส่งคืนสำเนา
    }

    /**
     * ล้างประวัติเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์ (optional)
     */
    clearHistory(event = null) {
      if (event) {
        this.history = this.history.filter(item => item.event !== event);
      } else {
        this.history = [];
      }
    }

    /**
     * สร้าง ID สำหรับ listener
     * @returns {string} ID ที่ไม่ซ้ำกัน
     */
    generateId() {
      return 'listener_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    }

    /**
     * รับจำนวนผู้ฟังสำหรับเหตุการณ์
     * @param {string} event - ชื่อเหตุการณ์
     * @returns {number} จำนวนผู้ฟัง
     */
    listenerCount(event) {
      return this.events[event] ? this.events[event].length : 0;
    }

    /**
     * รับชื่อเหตุการณ์ทั้งหมด
     * @returns {array} ชื่อเหตุการณ์ทั้งหมด
     */
    eventNames() {
      return Object.keys(this.events);
    }

    /**
     * ล้างผู้ฟังทั้งหมด
     * @param {string} event - ชื่อเหตุการณ์ (optional)
     */
    removeAllListeners(event = null) {
      if (event) {
        delete this.events[event];
      } else {
        this.events = {};
      }
    }
  }

  // เปิดเผยคลาส EventBus ทั่วโลก
  global.EventBus = EventBus;

})(typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : (typeof global !== 'undefined' ? global : (typeof self !== 'undefined' ? self : {}))));