代码之家  ›  专栏  ›  技术社区  ›  Manuel Huber

RxJS并非所有订阅者都收到所有事件

  •  2
  • Manuel Huber  · 技术社区  · 7 年前

    我正在做一个关于RxJS的练习。 发生了一些非常奇怪的事情:

      typoStream.subscribe(x => console.log('wont get executed'));
      wordCompletedStream.subscribe(nextStream);
      typoStream.subscribe(x => console.log('will get executed'));
    

    当应用程序运行第一个控制台时。日志不会打印,第二个会打印。
    不管流是什么以及它们是如何相互作用的——这永远不会发生,对吗?为什么当我订阅一个observable时它很重要——它不应该向每个订阅者发送事件吗?

    如果你想试试: http://embed.plnkr.co/xb8Yimo5RcYGPtgClYgY/

    正确键入显示的单词,您可以看到“错误”的作用。但这并不是每次都会发生——只是大多数时候。

    以下是水流: https://photos.app.goo.gl/Z4cpKzekAIuKzMF93

    1 回复  |  直到 7 年前
        1
  •  3
  •   GregL    7 年前

    我对你发布的代码进行了尝试,关键的解决方法是正确地多播 checkWord 可观察。你可以用 .publish().refCount() 就像你为 wordStream ,也可以使用做相同事情的快捷方式, .share() .

      const checkStream = wordStream.combineLatest(inputStream).share();
    

    这样做的原因是,如果没有它,多个订阅 checkStream 或从中派生的任何流,例如 typoStream wordCompletedStream 每个都会触发对 文字流 可观察(这是正确的多播,因此不会发出新请求)和 inputStream observable,它将在输入上注册新的事件侦听器。

    .共享() 运营商,订阅多少并不重要 支票流 或派生的观察值,只有第一个会触发订阅 输入流 .

    请注意,在此修复之后,两个订阅者都没有 打印流 打印流 . 当输入不正确的字符时,两者都将触发。

    Forked Plunkr here

    或者参见下面的代码片段:

    (() => {
    
      // --- 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>