WorkBus.js 等别人工作结束

/* ============================================================================
 * WorkBus.js — Promise 通信总线(ES6 模块,Node / 浏览器双端)
 *
 * 通信模式:resolve / waitEnd(Promise 握手)
 *   - WorkBus.resolve(event, payload)     发布(解决)事件:缓存最后 payload,唤醒全部等待者
 *   - WorkBus.waitEnd(event, [timeout])   等待事件:已解决立即返回缓存值;
 *                                         否则挂起,直到被 resolve 唤醒;可带超时
 *   - 时序无关:先 resolve 后 waitEnd → 命中缓存立即返回;
 *               先 waitEnd 后 resolve  → 挂起等待、发布时唤醒
 *   - 无 Node 专属依赖,Node / 浏览器通用(仅默认导出)
 *
 * 用法(Node):
 *   import WorkBus from "./WorkBus.js";
 * 用法(浏览器):
 *   <script type="module">
 *     import WorkBus from "./WorkBus.js";
 *   </script>
 *
 *   WorkBus.resolve("init", { verbose: 1 });
 *   const res = await WorkBus.waitEnd("init");
 * ========================================================================== */

/* 事件名 -> { resolved, payload, waiters: Set<{resolve, reject, timer}> } */
const states = new Map();

function getState(event) {
  if (!states.has(event)) {
    states.set(event, { resolved: false, payload: undefined, waiters: new Set() });
  }
  return states.get(event);
}

/**
 * 发布(解决)事件:缓存最后 payload,唤醒所有 waitEnd 等待者。
 * @param {string} event   事件名(非空字符串)
 * @param {*}      payload 负载数据
 */
function resolve(event, payload) {
  if (typeof event !== "string" || event.length === 0) {
    throw new TypeError("[WorkBus] resolve: event 必须是非空字符串");
  }
  const st = getState(event);
  st.resolved = true;
  st.payload = payload;
  for (const w of st.waiters) {
    if (w.timer) clearTimeout(w.timer);
    w.resolve(payload);
  }
  st.waiters.clear();
}

/**
 * 等待事件:已 resolve 过则立即返回缓存值,否则挂起等待下一次 resolve。
 * @param {string} event     事件名
 * @param {number} [timeout] 超时毫秒;缺省或 <=0 表示永不超时
 * @returns {Promise<*>} payload;超时则 reject
 */
function waitEnd(event, timeout) {
  if (typeof event !== "string" || event.length === 0) {
    throw new TypeError("[WorkBus] waitEnd: event 必须是非空字符串");
  }
  const st = getState(event);
  if (st.resolved) return Promise.resolve(st.payload);

  return new Promise((resolve, reject) => {
    const w = { resolve, reject, timer: null };
    st.waiters.add(w);
    if (typeof timeout === "number" && timeout > 0) {
      w.timer = setTimeout(() => {
        st.waiters.delete(w);
        reject(new Error(`[WorkBus] 等待事件 "${event}" 超时(${timeout}ms)`));
      }, timeout);
    }
  });
}

const WorkBus = { resolve, waitEnd };
export default WorkBus;

/* ============================================================================
 * 使用例子:node WorkBus.js 直接运行即自测;浏览器加载或被 import 时不执行
 *   ① "init"      先 resolve 后 waitEnd → 命中缓存,立即返回
 *   ② "getConfig" 先 waitEnd 后 resolve  → 挂起等待,1s 后发布唤醒
 * ========================================================================== */
// WorkBus.resolve("init", { verbose: 1 }); // 先发布 → 缓存
// setTimeout(function () {
//   WorkBus.resolve("getConfig", { verbose: 2 }); // 1s 后发布,唤醒 main()
// }, 1000);
// setTimeout(function () {
//   WorkBus.resolve("getConfig", { verbose: 3 }); // 2s 后再次发布:仅更新缓存(此时已无等待者)
// }, 2000);
//
// async function main() {
//   const res = await WorkBus.waitEnd("init"); // 已有缓存 → 立即返回
//   console.log(res);
//   const res2 = await WorkBus.waitEnd("getConfig"); // 尚未发布 → 挂起等待,1s 后被唤醒
//   console.log(res2);
//   const res3 = await WorkBus.waitEnd("getConfig"); // 已发布 → 命中缓存,立即返回
//   console.log(res3);
// }
// main();

 

posted @ 2026-10-23 10:38  无工时代  阅读(3)  评论(0)    收藏  举报