如何使用rxjs定期检查实时连接?

时间:2017-02-08 14:23:45

标签: javascript websocket rxjs rxjs5

我使用rxjs来处理websocket连接

var socket = Rx.Observable.webSocket('wss://echo.websocket.org')
socket.resultSelector = (e) => e.data

我希望定期(5s)发送ping消息并等待3秒以接收pong响应并在未收到响应的情况下订阅该流。

我试着没有成功。我承认我有点失落,所有操作员都可以处理超时,延迟或限制。

// periodically send a ping message
const ping$ = Rx.Observable.interval(2000) 
  .timeInterval()
  .do(() => socket.next('ping'))

const pong$ = socket  
  .filter(m => /^ping$/.test(`${m}`))  
  .mergeMap( 
    ping$.throttle(2000).map(() => Observable.throw('pong timeout'))
  ) 

 pong$.subscribe(
   (msg) => console.log(`end ${msg}`),
   (err) => console.log(`err ${err}`),
   () => console.log(`complete`)
 )

但不幸的是,没有发送ping。

我也试图在没有成功的情况下实现这一目标。

const ping$ = Rx.Observable.interval(2000) 
  .timeInterval()
  .do(() => socket.next('ping'))


const pong$ = socket  
  .filter(m => /^ping$/.test(`${m}`)) 

const heartbeat$ = ping$
  .debounceTime(5000) 
  .mergeMap(() => Rx.Observable.timer(5000).takeUntil(pong$))

heartbeat$.subscribe(
  (msg) => console.log(`end ${msg}`),
  (err) => console.log(`err ${err}`),
  () => console.log(`complete`)
)

任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:1)

您可以使用race()运算符始终只连接到首先发出的Observable:

function sendMockPing() {
  // random 0 - 5s delay
  return Observable.of('pong').delay(Math.random() * 10000 / 2);
}

Observable.timer(0, 5000)
  .map(i => 'ping')
  .concatMap(val => {
    return Observable.race(
      Observable.of('timeout').delay(3000),
      sendMockPing()
    );
  })
  //.filter(response => response === 'timeout') // remove all successful responses
  .subscribe(val => console.log(val));

查看现场演示:https://jsbin.com/lavinah/6/edit?js,console

这随机模拟了0 - 5s的回复。当响应时间超过{3}时,Observable.of('timeout').delay(3000)首先完成,timeout字符串由concatMap()传递给其观察者。

答案 1 :(得分:0)

我终于找到了基于mergeMaptakeUntil

的解决方案

我最初的错误是使用ping $作为我的heartBeat $的输入,我应该使用$ pong

// define the pong$
const pong$ = socket  
  .filter(m => /^ping$/.test(`${m}`)) 
  .share() 

//使用share()因为pong $被使用了两次

const heartbeat$ = pong$
  .startWith('pong') // to bootstrap the stream
  .debounceTime(5000) // wait for 5s after the last received pong$ value
  .do(() => this.socket.next('ping')) // send a ping
  .mergeMap(() => Observable.timer(3000).takeUntil(pong$))
// we merge the current stream with another one that will 
// not produce value while a pong is received before the end of the 
// timer

heartbeat$.subscribe(
  (msg) => console.log(`handle pong timeout`),
)

答案 2 :(得分:0)

heartbeat $函数下面的函数返回一个可观察的声音,您可以连续收听

1)每5000毫秒的每次来回latency value(socket.receive-socket.send时间)

2)-1,如果往返行程超出阈值(例如3000ms)

即使发出了latency value,您仍将继续收到-1-1,这使您可以灵活地决定要做什么^。^

heartbeat$(pingInterval: number, pongTimeout: number) {
  let start = 0;

  const timer$ = timer(0, pingInterval).pipe(share());

  const unsub = timer$.subscribe(() => {
    start = Date.now();
    this.ws.next('ping');
  });

  const ping$ = this.ws$.pipe(
    switchMap(ws =>
      ws.pipe(
        filter(m => /^ping$/.test(`${m}`)),
        map(() => Date.now() - start),
      ),
    ),
    share(),
  );

  const dead$ = timer$.pipe(
    switchMap(() =>
      of(-1).pipe(
        delay(pongTimeout),
        takeUntil(ping$),
      ),
    ),
  );

  return merge(ping$, dead$).pipe(finalize(() => unsub.unsubscribe()));
}

heartbeat$(5000, 3000).subscribe(
  (latency) => console.log(latency) // 82 83 82 -1 101 82 -1 -1 333 ...etc
)