You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Typescript+RxJs中拦截Promise异步事件传播并过滤ClientID

问题分析与解决方案

核心问题

当前实现存在两个关键问题:

  1. 全局Subject引发并发干扰:ssoEvent是全局共享的Subject,所有调用ssoService.call()的请求都会订阅同一个流。当某一弹窗返回消息时,所有等待中的订阅都会收到这条消息,不符合自身clientID的请求会持续阻塞,直到匹配的消息出现,导致并发请求互相干扰。
  2. 重复绑定message事件:每次调用call()都会重复给window绑定message事件,同一条消息会被多次处理并发送到Subject,进一步加剧并发逻辑混乱。

修正后的实现代码

1. 重构sso.service.ts

让每个call()请求创建独立的Observable流,避免重复绑定事件,同时关联弹窗实例以优化体验:

import { Observable, Subscriber } from 'rxjs';
import { take } from 'rxjs/operators';

export class SsoService {
  // 全局仅绑定一次基础消息事件
  constructor() {
    window.addEventListener("message", this.globalMessageHandler.bind(this), false);
  }

  public call(targetClientId: string): Observable<any> {
    const urlPage = this.getUrl();
    const ssoPopup = window.open(urlPage);

    return new Observable((subscriber: Subscriber<any>) => {
      // 当前请求专属的消息处理逻辑
      const specificMessageHandler = (event: MessageEvent) => {
        if (event.origin !== window.location.origin) return;
        
        if (event.data?.success && event.data?.result?.clientId === targetClientId) {
          subscriber.next(event.data.result);
          subscriber.complete();
          ssoPopup?.close();
        }
      };

      // 绑定当前请求的专属处理函数
      window.addEventListener("message", specificMessageHandler, false);

      // 订阅销毁时清理资源
      return () => {
        window.removeEventListener("message", specificMessageHandler);
        ssoPopup?.close();
      };
    }).pipe(take(1));
  }

  // 全局消息处理(可选,用于日志等通用逻辑)
  private globalMessageHandler(event: MessageEvent): void {
    // 可添加全局日志、异常监控等逻辑
  }

  private getUrl(): string {
    // 返回你的SSO页面地址
    return "/sso-auth-page";
  }
}

2. 修正parent.component.ts

调用call()时传入当前clientID,直接获取匹配的结果,简化组件内逻辑:

private async instance(): Promise<void> {
  if (this.exist()) {
    await this.getSSO();
  }
}

private async getSSO(): Promise<void> {
  const currentClientID = this.getClientID();
  this.loadingSSO = true;
  
  try {
    const result = await this.ssoService.call(currentClientID).toPromise();
    // 此处result已确保匹配当前clientID
    console.log("SSO验证成功:", result);
  } finally {
    this.loadingSSO = false;
  }
}

private exist(): boolean {
  // 返回是否需要触发SSO的判断逻辑
  return true;
}

private getClientID(): string {
  // 返回当前客户端ID
  return "your-client-id";
}

关键改进点

  • 请求独立隔离:每个call()生成专属Observable与消息处理函数,仅响应匹配自身clientID的消息,彻底避免并发请求互相干扰。
  • 事件绑定优化:全局仅绑定一次基础事件,每个请求临时绑定专属处理函数,订阅销毁时自动清理,防止内存泄漏。
  • 弹窗生命周期管理:弹窗会在收到匹配消息或订阅取消时自动关闭,提升用户体验。
  • 组件逻辑简化:组件无需再处理过滤逻辑,直接获取符合条件的结果,代码更简洁易维护。

内容的提问来源于stack exchange,提问作者fixesMyCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:57:18