如何使用RxJS实现队列?

时间:2018-02-02 12:45:52

标签: node.js rxjs rxjs5

我设计了一个API(在NodeJS中),例如,它接收用户ID列表,并访问Google Firebase的API以向这些用户发送通知。

考虑到Firebase对您可以同时发送的请求数量有一个配额限制,我缓冲了ID并将请求逐个发送到Firebase,延迟时间为2秒。这里有一些简化的示例代码供您解释:

app.post('/send-request', (req, res, next) =>{
  const userIds = req.body.userIds;
  ‎const streams = userIds.map((userId) => {
‎    return Observable
‎      .fromPromise(fetch(`firebase url`, {
‎          method: 'POST', 
‎          headers: ..., 
          body: ... 
‎        }))
‎        .delay(2000) 
‎        .retryWhen(attempts => { 
‎          return attempts.zip(Rx.Observable.range(1, 4))
            .mergeMap(([error, i]) => { 
              if (i > 3) { 
                return Rx.Observable.throw(error); 
            } 
              console.log(`Wait ${i} seconds, then retry!`); 
              return Rx.Observable.timer(i * 1000); 
            });
      ‎  });
  });


  const stream = Observable.merge(...streams);
‎  stream.subscribe();
});

这可以处理单个请求由许多用户组成的情况。但是,如果我的API同时收到类似的请求,它一定会失败。

因此,我希望将所有这些用户ID缓冲在队列中,此队列可以继续接收越来越多的用户ID缓冲它们,同时“排除”顶部'通过以稳定的速率向Firebase发送请求来排队。但是,我不知道如何使用RxJS。我必须使用调度程序吗?或者实际上是否比使用Rx更好的解决方案?

注意:我理解Javascript是单线程的,因此它并不完全是并发性的,我只使用了这个词,所以你可以更好地理解它。

1 个答案:

答案 0 :(得分:0)

我认为我设法提出了一些建议,关键部分是使用Subject发布值,然后使用zip运算符定期发出值。如果我可以按需获取值,它会更好,但是当前的解决方案已经比我原来的方法好得多。

const subject = new Rx.Subject();
const stream = subject
  .zip(Rx.Observable.interval(3000), function(a, b) { return a; });

stream.subscribe(
  (x) => { console.log(`onNext: ${val}`); },  
  (e) => { console.log(`onError: ${e}`); },
  () => { console.log('onCompleted'); });

我使用VueJS为一个小小的演示构建了一个简单的网页。



const app = new Vue({
  el: '#app',
  data: {
    subject: undefined,
    stream: undefined,
    count: 0,
    emitHistory: [],
    disableBtn: true
  },
  created() {
    console.log('created');
    this.subject = new Rx.Subject();
    this.stream = this.subject
      .zip(Rx.Observable.interval(3000), function(a, b) { return a; })
      // .observeOn(Rx.Scheduler.queue); // not working
    this.stream.subscribe(
    	(val) => { 
      	console.log(`onNext: ${val}`);
        this.emitHistory.push(val);
        if (val === this.count) {
        	this.disableBtn = false;
        } else {
	        this.disableBtn = true;
        }
      },
      (e) => { console.log(`onError: ${e}`); },
      () => { console.log('onCompleted'); });
  },
  methods: {
    clickHandler() {
      this.count++;
      this.subject.onNext(this.count);
    },
    clear() {
    	this.count = 0;
      this.emitHistory = [];
    }
  }
});

/* 
Rx.Observable
  .fromArray([1,2,3])
  .zip(Rx.Observable.interval(500), function(a, b) { return a; })
  .subscribe(
    function(x) { document.write(x + '<br \>'); },  
    null,  
    function() { document.write("complete"); }); 
*/
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.js"></script>
<div id="app">
  <button @click="clickHandler()">Click me</button>
  <button @click="clear()" v-bind:disabled="disableBtn">Clear</button>
  <div>
    <h5>Count: {{count}}</h5>
  </div>
  <div>
    <ul>
      <li v-for="(item, idx) in emitHistory" v-bind:key="idx">{{item}}</li>
    </ul>
  </div>
</div>
&#13;
&#13;
&#13;