关于angular:concatMap,即使有多个订阅者,也只能共享一次播放一系列可观察的对象

concatMap and share to play a series fo observables only once even if multiple subscribers

我正在尝试连续执行一些可观察性,这与concatMap一样,但是我希望它们只能执行一次,即使我有多个订阅者,我也尝试了share和shareReplay运算符,但它们似乎都不起作用。

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { EMPTY, Observable, BehaviorSubject, of, from, concat, timer } from 'rxjs';
import { concatMap, map, switchMap, tap, filter, take, share, shareReplay, exhaust, publishLast, refCount } from 'rxjs/operators';


const refreshAccessToken = () => {
  console.log("Start refresh access token")
  return timer(2000);
}

const connect = () => {
  console.log("Start connect")
  return timer(3000)
}

const authent = () => {
  console.log("Start authent")
  return timer(2000)
}

let isAuthenticated = false;
let isExpired = true;

function initSocket(): Observable {
  if (!isAuthenticated) {
    let observable$ = of(1);
    if (isExpired) {
      observable$ = refreshAccessToken();
    }
    return observable$.pipe(
      concatMap(() => connect()),
      concatMap(() => authent()),
      tap(() => {
        isAuthenticated = true;
        console.log("Tap")
      }),
      share()
    );
  }
  return of(1);
}


initSocket().subscribe(() => console.log("Finished 1"))

initSocket().subscribe(() => console.log("Finished 2"))

您可以在https://stackblitz.com/edit/rxjs-4memxw?embed=1&file=index.ts中找到我的示例

结果是:

1
2
3
4
5
6
7
8
9
10
Start refresh access token
Start refresh access token
Start connect
Start connect
Start authent
Start authent
Tap
Finished 1
Tap
Finished 2

但是我正在寻找的是:

1
2
3
4
5
6
Start refresh access token
Start connect
Start authent
Tap
Finished 1
Finished 2

也许已经回答了,但是我看不到我所缺少的,如果有人可以帮助,那就太好了。

编辑:

我会解释我的需求,也许我以错误的方式解决了这个问题。
我正在编码一个包装一个socket.io客户端的服务类,该类中每个发出或侦听socket.io事件的函数都必须检查我们是否已连接并通过socket.io进行身份验证。

在每个订阅上,多个可观察对象都需要执行以下操作:

  • 检查是否连接
  • 如果不是,请检查访问令牌是否仍然存在
    有效,如果是,请转到4
  • 如果不是,请刷新访问令牌
  • 创建socket.io连接并等待连接事件
  • 在连接事件上发送access_token并等待响应
  • 如果身份验证成功,则继续观察下一个
  • 在这一系列的可观察对象中,第一个用户将执行所有可观察对象,如果第二个用户在观察对象正在执行时到达等待相同的结果,则不要两次发起连接。

    如果在连接完成后到达第三位或更多位,我们仍然需要检查变量isAuthenticated是否已更改,如果为false,则像第一个订阅者一样操作,如果为true,则继续。

    希望它有助于阐明我在寻找什么。


    您可能希望将数据分配给一个可观察对象,然后订阅该可观察对象,而不是调用initFunction()来创建与订阅者一样多的可观察对象。 但是通过将数据分配给可观察的并订阅该数据,可以防止这种情况的发生,也可以使用shareReplay

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    let obs$ = new Observable();

    function initSocket(): void {
      if (!isAuthenticated) {
        let observable$ = of(1);
        if (isExpired) {
          observable$ = refreshAccessToken();
        }
        obs$ = observable$.pipe(
          concatMap(() => connect()),
          concatMap(() => authent()),
          tap(() => {
            isAuthenticated = true;
            console.log("Tap")
          }),
          shareReplay()
        );
      }
    }

    initSocket();

    obs$.subscribe(() => console.log("Finished 1"))

    obs$.subscribe(() => console.log("Finished 2"))

    setTimeout(() => {
      obs$.subscribe(() => console.log("Finished 3 after 20 seconds"))
    }, 20000)

    叠砖