RxJS:生产者 - 消费者中止

时间:2015-02-26 17:22:20

标签: javascript reactive-programming rxjs reactive-extensions-js

我在RxJS中遇到了一个特殊的生产者消费者问题:生产者慢慢生产元素。消费者正在请求元素,并且经常必须等待生产者。这可以通过压缩生产者和请求流来实现:

var produce = getProduceStream();
var request = getRequestStream();

var consume = Rx.Observable.zipArray(produce, request).pluck(0);

有时请求会中止。生成的元素只应在未中止请求后使用:

produce:  -------------p1-------------------------p2--------->
request:  --r1--------------r2---------------r3-------------->
abort:    ------a(r1)------------------a(?)------------------>
consume:  ------------------c(p1, r2)-------------c(p2, r3)-->

第一个请求r1将使用第一个生成的元素p1,但r1会被a(r1)中止,然后才会消耗p1p1生成并在第二次请求c(p1, r2)上消耗r2。第二次中止a(?)被忽略,因为之前没有发出未答复的请求。第三个请求r3必须等待下一个生成的元素p2,并且在生成p2之前不会中止。因此,p2在生成后立即消耗c(p2, r3)

如何在RxJS中实现这一目标?

修改 我在jsbin上创建了一个带有QUnit测试的example。您可以编辑功能createConsume(produce, request, abort)以尝试/测试您的解决方案。

该示例包含previously accepted answer的函数定义。

3 个答案:

答案 0 :(得分:3)

这个(核心思想减去细节)通过了你的JSBin测试:

var consume = request
  .zip(abort.merge(produce), (r,x) => [r,x])
  .filter(([r,x]) => isNotAbort(x))
  .map(([r,p]) => p);

JSBin code

答案 1 :(得分:2)

我无法完全了解如何使用现有的运营商。以下是Observable.create()

的使用方法
return Rx.Observable.create(function (observer) {
  var rsub = new Rx.SingleAssignmentDisposable();
  var asub = new Rx.SingleAssignmentDisposable();
  var psub = new Rx.SingleAssignmentDisposable();
  var sub = new Rx.CompositeDisposable(rsub, asub, psub);
  var rq = [];
  var pq = [];
  var completeCount = 0;
  var complete = function () {
    if (++completeCount === 2) {
      observer.onCompleted();
    }
  };
  var consume = function () {
    if (pq.length && rq.length) {
      var p = pq.shift();
      var r = rq.shift();
      observer.onNext('p' + p);
    }
  };

  rsub.setDisposable(request.subscribe(
    function (r) {
      rq.push(r);
      consume();
    },
    function (e) { observer.onError(e); },
    complete));

  asub.setDisposable(abort.subscribe(
    function (a) {
      rq.shift();
    },
    function (e) { observer.onError(e); }
  ));

  psub.setDisposable(produce.subscribe(
    function (p) {
      pq.push(p);
      consume();
    },
    function (e) { observer.onError(e); },
    complete));


  return sub;
});

http://jsbin.com/zurepesijo/1/

答案 2 :(得分:0)

此解决方案忽略了不遵循未答复请求的中止:

const {merge} = Rx.Observable;

Rx.Observable.prototype.wrapValue = function(wrapper) {
    wrapper = (wrapper || {});
    return this.map(function (value) {
        wrapper.value = value;
        return wrapper;
    });
};

function createConsume(produce, request, abort) {
  return merge(
            produce.wrapValue({type: 'produce'}),
            request.wrapValue({type: 'request'}),
            abort.wrapValue({type: 'abort'})
         )
         .scan(
            [false, []],
            ([isRequest, products], e) => {
                // if last time the request was answered
                if (isRequest && products.length) {
                    // remove consumed product
                    products.shift();
                    // mark request as answered
                    isRequest = false;
                }
                if (e.type === 'produce') {
                    // save product to consume later
                    products.push(e.value);
                } else {
                    // if evaluated to false, e.type === 'abort'
                    isRequest = (e.type === 'request');
                }
                return [isRequest, products];
            }
         )
         .filter( ([isRequest, products]) => (isRequest && products.length) )
         .map( ([isRequest, products]) => products[0] ); // consume
}

Code在最新的JSBin测试中。