我有一个应用程序通过串口与设备通信。每个发送的命令由包含状态/答案的数据事件回答。基本上有更改设备的命令和只返回状态的命令。每次最后一个命令被应答时(所以在接收数据时)应用程序应发送下一个命令或作为默认查询状态。我试图用rxjs来模拟它。 我的想法是有一个命令observable和一个从data事件派生的数据observable。这两个应该以这样的方式组合,结果observable仅在有数据时发出值,并将其与命令或默认命令(请求状态)组合,如果命令流中没有命令。
data: ---------d---d-----d---------d------d-------
command: --c1---c2----------------------c3-----------
______________________________________________________
combined ---------c1--c2----dc--------dc-----c3
dc是默认命令。也不应该丢失任何命令。
目前我有一个匿名主题的实现,我自己实现了observable和observer。从数组中的命令流收集命令,订阅数据事件,使用onNext手动发布数据并从数组或默认发送下一个命令。这有效,但我觉得这可以用rxjs更优雅地表达。
一种方法是使用单独的default_command流,每100ms重复一次默认命令。这与命令流合并,然后用数据流压缩。这里的问题是合并的命令流,因为它堆积了默认命令,但默认命令只应用,如果没有其他命令。
答案 0 :(得分:0)
我能想到的只有:
我们可以将它包装成一个通用的可观察运算符。我的姓名不好,所以我称之为zipWithDefault
:
Rx.Observable.prototype.zipWithDefault = function(bs, defaultB, selector) {
var source = this;
return Rx.Observable.create(function(observer) {
var sourceSubscription = new Rx.SingleAssignmentDisposable(),
bSubscription = new Rx.SingleAssignmentDisposable(),
subscriptions = new Rx.CompositeDisposable(sourceSubscription, bSubscription),
bQueue = [],
mappedSource = source.map(function(value) {
return selector(value, bQueue.length ? bQueue.shift() : defaultB);
});
bSubscription.setDisposable(bs.subscribe(
function(b) {
bQueue.push(b);
},
observer.onError.bind(observer)));
sourceSubscription.setDisposable(mappedSource.subscribe(observer));
return subscriptions;
});
};
并像这样使用它:
combined = dataStream
.zipWithDefault(commandStream, defaultCommand, function (data, command) {
return command;
});
答案 1 :(得分:0)
我认为sample
运营商是您最好的选择。不幸的是,它没有内置的默认值,因此你必须从现有的运算符中推出自己的值:
Rx.Observable.prototype.sampleWithDefault = function(sampler, defaultValue){
var source = this;
return new Rx.AnonymousObservable(function (observer) {
var atEnd, value, hasValue;
function sampleSubscribe() {
observer.onNext(hasValue ? value : defaultValue);
hasValue = false;
}
function sampleComplete() {
atEnd && observer.onCompleted();
}
return new Rx.CompositeDisposable(
source.subscribe(function (newValue) {
hasValue = true;
value = newValue;
}, observer.onError.bind(observer), function () {
atEnd = true;
}),
sampler.subscribe(sampleSubscribe, observer.onError.bind(observer), sampleComplete)
);
}, source);
}
您可以使用controlled
运算符来实现排队行为。因此,您的最终数据链将如此:
var commands = getCommandSource().controlled();
var pipeline = commands
.sampleWithDefault(data, defaultCommand)
.tap(function() { commands.request(1); });
以下是一个完整的例子:
Rx.Observable.prototype.sampleWithDefault = function(sampler, defaultValue) {
var source = this;
return new Rx.AnonymousObservable(function(observer) {
var atEnd, value, hasValue;
function sampleSubscribe() {
observer.onNext(hasValue ? value : defaultValue);
hasValue = false;
}
function sampleComplete() {
atEnd && observer.onCompleted();
}
return new Rx.CompositeDisposable(
source.subscribe(function(newValue) {
hasValue = true;
value = newValue;
}, observer.onError.bind(observer), function() {
atEnd = true;
}),
sampler.subscribe(sampleSubscribe, observer.onError.bind(observer), sampleComplete)
);
}, source);
}
var scheduler = new Rx.TestScheduler();
var onNext = Rx.ReactiveTest.onNext;
var onCompleted = Rx.ReactiveTest.onCompleted;
var data = scheduler.createHotObservable(onNext(210, 18),
onNext(220, 17),
onNext(230, 16),
onNext(250, 15),
onCompleted(1000));
var commands = scheduler.createHotObservable(onNext(205, 'a'),
onNext(210, 'b'),
onNext(240, 'c'),
onNext(400, 'd'),
onCompleted(800))
.controlled(true, scheduler);
var pipeline = commands
.sampleWithDefault(data, 'default')
.tap(function() {
commands.request(1);
});
var output = document.getElementById("output");
pipeline.subscribe(function(x) {
var li = document.createElement("li");
var text = document.createTextNode(x);
li.appendChild(text);
output.appendChild(li);
});
commands.request(1);
scheduler.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.testing.js"></script>
<div>
<ul id="output" />
</div>
答案 2 :(得分:0)
这可以通过使用scan函数来解决。在累计值中,存储的命令尚未收到任何数据响应。
var result = Rx.Observable
.merge(data, command)
.scan(function (acc, x) {
if (x === 'd') {
acc.result = acc.commands.length > 0 ? acc.commands.shift() : 'dc';
} else {
acc.result = '';
acc.commands.push(x);
}
return acc;
}, {result: '', commands: []})
.map(function (x) {
return x.result;
})
.filter(function (x) {
return x !== '';
});
请在此处找到更详细的信息:http://jsbin.com/tubade/edit?html,js,console