我正在做一个关于RxJS的练习。 并且发生了一些非常奇怪的事情:
typoStream.subscribe(x => console.log('wont get executed'));
wordCompletedStream.subscribe(nextStream);
typoStream.subscribe(x => console.log('will get executed'));
当应用程序运行时,第一个console.log将不会被打印,第二个将会打印 无论流是什么以及它们如何相互作用 - 这都不应该发生,对吧?当我订阅一个observable时,为什么它很重要 - 不管它是否应该向每个订阅者发送事件呢?
如果你想尝试一下: http://embed.plnkr.co/xb8Yimo5RcYGPtgClYgY/
正确输入显示的单词,您可以看到“错误”。但它并不是每次都发生 - 只是大部分时间。
答案 0 :(得分:2)
我玩了你发布的代码,关键修复是正确组播checkWord
observable。您可以像.publish().refCount()
一样使用wordStream
执行此操作,也可以使用执行相同操作的快捷方式.share()
。
const checkStream = wordStream.combineLatest(inputStream).share();
这样做的原因是没有它,checkStream
的多个订阅或从它派生的任何流,例如typoStream
和wordCompletedStream
都会触发wordStream
和inputStream
的新订阅1}} observable(正确多播,因此没有新请求)和.share()
observable,它将在输入上注册新的事件监听器。
使用checkStream
运算符,对inputStream
或派生的observable进行了多少订阅并不重要,只有第一个订阅会触发对typoStream
的订阅。< / p>
请注意,在此修复之后,typoStream
的两个订阅者都不会为正确输入的单词触发。这是我所期望的一个名为(() => {
// --- UI Stuff, NO NEED TO TOUCH THESE --- //
const wordField = $('#TotDWord');
const inputField = $('#TotDInput');
// ----------------------------------------- //
// A stream of the users string inputs
const inputFieldStream = Rx.Observable.fromEvent(inputField, 'keyup')
.map(x => x.target.value).distinctUntilChanged();
// This stream is used to represent the users unput - we don't use the
// inputFieldStream directly because we want to manually send values aswell
const inputStream = new Rx.Subject();
// Feed the stream from the field into our inputStream
inputFieldStream.subscribe(inputStream);
// A stream that allows us to manually trigger that we need a new word
const nextStream = new Rx.Subject();
// When we want the next word we need to reset the users input
nextStream.subscribe(() => {
inputField.val('');
inputStream.onNext('');
});
// This stream calls a server for a new random word every time the nextStream emits an event. We startWith a value to trigger the first word
const wordStream = nextStream.startWith('')
.flatMapLatest(getRandomWord)
// publish & refCount cache the result - otherwise every .map on wordStream would cause a new HTTP request
.publish().refCount();
// When there is a new word, we display it
wordStream.subscribe(word => {
wordField.empty();
wordField.append(word);
});
// Checkstream combines the latest word with the latest userinput. It emits an array, like this ['the word', 'the user input'];
const checkStream = wordStream.combineLatest(inputStream).share();
// Emits an event if the user input is not correct
const typoStream = checkStream.filter(tuple => {
const word = tuple[0];
const input = tuple[1];
return !word.startsWith(input);
});
// When there is a typo we need a new word
typoStream.subscribe(nextStream);
// Emits an event when the user has entered the entire word correctly
const wordCompletedStream = checkStream.filter(tuple => {
const word = tuple[0];
const input = tuple[1];
return word == input;
});
/**
* THIS WILL (MOST OF THE TIME) NOT FIRE WHEN YOU COMPLETE A WORD
*/
typoStream.subscribe(x => console.log('wont get executed'));
// Whenever the word is completed, request a new word
wordCompletedStream.subscribe(nextStream);
/**
* THIS WILL FIRE WHEN YOU COMPLETE A WORD
*/
typoStream.subscribe(x => console.log('will get executed'));
// Calls a server for a random word
// returns a promise
function getRandomWord() {
return $.ajax({
// Change the URL to cause a 404 error
url: 'https://setgetgo.com/randomword/get.php'
}).promise();
}
})();
的可观察对象。输入错误字符时,两者都会触发。
或者看下面的代码:
<script data-require="jquery" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<div>
<h1>Exercise: Typing of the Dead</h1>
<div>
Type the given word correctly and watch the console. Most of the time 1 of the 2 subscriptions on the typoStream will fire (when there should fire none).
</div>
<br />
<div id="TotDWord"></div>
<input type="text" name="" id="TotDInput" value="" /><span>Highscore: </span><span id="TotDHighscore"></span>
<div id="TotDScore"></div>
</div>
<script>
console.clear();
</script>
&#13;
<form id="form1" runat="server">
<asp:Repeater runat="server" ID="rptrInfo" OnItemCommand="Repeater1_ItemCommand" OnItemDataBound="rptrInfo_ItemDataBound">
<ItemTemplate>
<asp:Image ID="imgImage" runat="server" ImageUrl='<%#Eval("ImageUrl") %>' />
<asp:Panel id="pnlVideo" runat="server">
<video controls height="200" width="385" id="v<%#Eval("Info_Id") %>">
<source src="<%#Eval("VideoUrl") %>" type="video/mp4" />
</video>
</asp:Panel>
</ItemTemplate>
</asp:Repeater>
&#13;