订阅时保留订单

时间:2015-09-02 22:17:07

标签: javascript rxjs

我正在尝试为接收命令的服务器实现自动重新连接客户端,然后使用单个字节进行回复。但是,问题是在处理命令时无法向服务器发送任何其他命令。所以我需要以某种方式序列化命令,是否可以在RxJS中以务实的方式实现?

const onClient = new BehaviourSubject(...) // Auto-reconnecting client

function sendCommand(cmd) {
  return onClient
     .concatMap(client => {
       client.write(cmd + '\r\n')
       return Rx.Observable.fromEvent(client, 'data').take(1)
     })
}

sendCommand('CMD1').subscribe(x => console.log(x))
sendCommand('CMD2').subscribe(x => console.log(x)) // Oops, sent a command while another one is active...

这是一种可能的解决方案,缺乏错误处理,看起来效率很低。

const input = new Rx.Subject()
const output = new Rx.Subject()

input.concatMap(({cmd, id})) => onClient
   .filter(client => client != null)
   .concatMap(client => {
     client.write(cmd + '\r\n')
     return Rx.Observable.fromEvent(client, 'data').take(1)
   })
   .map(value => ({value, id}))
   .subscribe(output)

function sendCommand(cmd) {
  const id = cuid()
  input.onNext(id)
  return output
    .filter(res => res.id === id)
    .map(res => res.value)
}

有关改进的更好的想法或建议吗?

2 个答案:

答案 0 :(得分:1)

这是我的直觉。我只使用过JavaRX,而这只是勉强。请注意,这假设您希望每次返回CMD1时都调用一次CMD2。

const onClient = new BehaviourSubject(...) // Auto-reconnecting client

function sendCommand(cmd) {
  return onClient
     .concatMap(client => {
       client.write(cmd + '\r\n')
       return Rx.Observable.fromEvent(client, 'data').take(1)
     })
}

sendCommand('CMD1').subscribe(function(x) {
  console.log(x);
  sendCommand('CMD2').subscribe(y => console.log(y))
});

对于它的价值,你可能想考虑使用Promises来做这些事情。我对Rx的理解是它对于复杂的异步数据流很有用,比如事件流。但如果您想要的只是异步部分,我相信Promises可能会更容易。我们正在考虑在Java项目中使用它,并认为它不是我们需要的。请参阅:When to Use Rx

我不知道你在做什么,但是在我看来,命令响应模式可能会更好地服务于Promises,特别是如果你期望你传入的lambda {{1只能被调用一次。

答案 1 :(得分:0)

这是我最终得到的相当复杂的尝试:

import stampit from 'stampit'
import Rx from 'rx'
import cuid from 'cuid'

    let input = new Rx.Subject()
    let output = new Rx.Subject()

    input
      .concatMap(({fn, id}) => Rx.Observable
          .defer(() => fn())
          .map(value => ({value, id}))
          .catch(error => Rx.Observable.return({error, id}))
          .concat(Rx.Observable.return({id})))
      .subscribe(output)

    async function enqueue(fn) {
        const id = cuid()
        input.onNext({fn, id})
        output
          .filter(res => res.id === id)
          .takeWhile(res => res.error || res.value)
          .concatMap(res => res.error
            ? Rx.Observable.throw(res.error)
            : Rx.Observable.return(res.value))
      }
    })

const onClient = new BehaviourSubject(...) // Auto-reconnecting client

function sendCommand(cmd) {
  return enqueue(() => onClient
     .concatMap(client => {
       client.write(cmd + '\r\n')
       return Rx.Observable.fromEvent(client, 'data').take(1)
     }))
}

sendCommand('CMD1').subscribe(x => console.log(x))
sendCommand('CMD2').subscribe(x => console.log(x))