如何一次处理RxJS流n个项目,一旦项目完成,再次自动填充回来?

时间:2016-07-26 23:28:17

标签: javascript rxjs backpressure

我有一个事件流,我想调用一个函数来返回每个事件的承诺,问题是这个函数非常昂贵,所以我想一次处理最多n个事件

这个鹅卵石图可能不对,但这就是我想要的:

---x--x--xxxxxxx-------------x------------->  //Events
---p--p--pppp------p-p-p-----p------------->  //In Progress
-------d--d--------d-d-dd------dddd-------->  //Promise Done

---1--21-2-34-----------3----4-3210--------   //QUEUE SIZE CHANGES

这是我到目前为止的代码:

var n = 4;
var inProgressCount = 0;

var events$ = Rx.Observable.fromEvent(produceEvent, 'click')
  .map((ev) => new Date().getTime());

var inProgress$ = events$.controlled();

var done$ = inProgress$      
  .tap(() => inProgressCount++)
  .flatMap((timestamp) => Rx.Observable.fromPromise(expensiveComputation(getRandomInt(1, 5) * 1000, timestamp)));

done$.subscribeOnNext((timestamp) => {
  inProgressCount--;
  inProgress$.request(Math.max(1, n - inProgressCount));
});

inProgress$.request(n);

此代码存在两个问题:

  1. 它使用了{side}更新的inProgressCount var 效果功能。
  2. 当我从受控流请求多于1个项目时,只会调用一次done $ subscription。这使得inProgressCount var无法正确更新,这最终会将队列限制为一次。
  3. 你可以看到它在这里工作: http://jsbin.com/wivehonifi/1/edit?js,console,output

    问题:

    1. 有更好的方法吗?
    2. 如何摆脱inProgressCount变量?
    3. 为什么在请求多个项目时只会调用一次完成的$ subscription?
    4. 更新:
      回答问题#3:switchMap与flatMapLatest相同,所以这就是为什么我只得到最后一个。将代码更新为flatMap而不是switchMap。

1 个答案:

答案 0 :(得分:3)

你实际上根本不需要使用背压。有一个名为flatMapWithMaxConcurrent的运算符可以为您执行此操作。它本质上是调用.map().merge(concurrency)的别名,它只允许一次最大数量的流。

我在这里更新了你的jsbin:http://jsbin.com/weheyuceke/1/edit?js,output

但我在下面注释了重要的一点:

const concurrency = 4;

var done$ = events$
  //Only allows a maximum number of items to be subscribed to at a time
  .flatMapWithMaxConcurrent(concurrency, 
    ({timestamp}) =>   
      //This overload of `fromPromise` defers the execution of the lambda
      //until subscription                    
      Rx.Observable.fromPromise(() => { 
        //Notify the ui that this task is in progress                                 
        updatePanelAppend(inProgress, timestamp);
        removeFromPanel(pending, timestamp);
        //return the task
        return expensiveComputation(getRandomInt(1, 5) * 1000, timestamp)
     }));