正确使用takeUntil()来停止observable

时间:2017-09-05 20:52:04

标签: rxjs observable observer-pattern rxjs5

我正在尝试创建2个observable,它基本上都是反向操作。它是一个服务发现端点,因此在启动应用程序时,必须尝试注册服务发现直到成功。所以我想创建一个像这样的可观察对象:

const create$ = Rx.Observable.create((observer) => {
    observer.next('Trying to create observation');
    sp.put(endpoint, { json: true }, (err, res, payload) => {
      err ? observer.error(err) : observer.complete();
    });
  });

当应用程序正在进行正常关闭时,我想进行反向操作。像这样:

const delete$ = Rx.Observable.create((observer) => {
    console.log('deleted subscribed');
    observer.next('Trying to delete observation');
    sp.delete(endpoint, { json: true }, (err, res, payload) => {
      err ? observer.error(err) : observer.complete();
    });
  });

所以我决定创建一个返回具有.create().delete()的对象的函数。我想要解决的问题是,当应用程序启动并尝试注册但无法访问服务发现端点时,应用程序启动它的正常关闭过程并调用.delete()操作,然后{{1} }操作不应该再运行了。

.create()

我遇到的问题是,当使用function observe({ url, version, serviceName }) { const endpoint = `/endpoint/${serviceName}/${version}/${encodeURIComponent(url)}`; const create$ = Rx.Observable.create((observer) => { observer.next('Trying to create observation'); sp.put(endpoint, { json: true }, (err, res, payload) => { err ? observer.error(err) : observer.complete(); }); }); const delete$ = Rx.Observable.create((observer) => { console.log('deleted subscribed'); observer.next('Trying to delete observation'); sp.delete(endpoint, { json: true }, (err, res, payload) => { err ? observer.error(err) : observer.complete(); }); }); return { create() { return create$.retry(Number.POSITIVE_INFINITY).takeUntil(delete$); // This is where I would want to takeUntil() }, delete({ interval = 5000, times = 0 } = {}) { return delete$.retry(times); }, } } 时,它会订阅.takeUntil(),它会开始执行删除操作,这会立即停止delete$观察。

我尝试过做create$,但它同时订阅了两者,所以不起作用。我也试过做takeUntil(Observable.merge(Observable.never(), delete$))第一个永远不会结束(:P),第二个永远不会订阅。

2 个答案:

答案 0 :(得分:4)

大多数Observable都是单播。这是一个冗长的主题,因此我将在大多数情况下遵循伟大的文章Hot vs Cold Observables,但总结一下:

  

COLD是您的observable创建生产者

的时候
// COLD
var cold = new Observable((observer) => {
  var producer = new Producer();
  // have observer listen to producer here
});
     

HOT是您的观察者关闭生产者

的时候
// HOT
var producer = new Producer();
var hot = new Observable((observer) => {
  // have observer listen to producer here
});

在您的示例中,这是一个重要的区别,因为create$delete$都很冷。正如您所发现的那样,为delete$提供takeUntil会导致delete$订阅,从而启动请求。

如果要保持代码的结构/ API原样,实现此目的的一种方法是使用被视为一种“通知程序”的主题。主题是多播和“热”(即使他们自己不做任何事情)。

function observe({ url, version, serviceName }) {
  // etc...

  const shutdown$ = new Subject(); // <---------------- create our notifier

  return {
    create() {
      return create$
        .retry(Number.POSITIVE_INFINITY)
        .takeUntil(shutdown$); // <-------------------- take our notifier
    },
    delete({ interval = 5000, times = 0 } = {}) {
      return Observable.defer(() => {
        shutdown$.next(); // <------------------------- notify
        return delete$.retry(times);
      });
    }
  };
}

我们使用了Observable.defer(),这样只要有人实际订阅了我们返回的Observable,我们就可以执行shutdown$.next()副作用。

答案 1 :(得分:1)

方法#1

您可以在.takeUntill中使用Observable而无需通过创建中间Subject实际订阅它。然后您可以订阅该主题而不是原始主题。像这样的东西:

const delete$ = new Subject();
...
create() {
  return create$
    .takeUntill(delete$);   
},
delete() {
  create(...).subscribe(delete$);
  return delete;
}

方法#2

但是在你的情况下,我认为创建主题会更好,这会在调用.delete时通知。像这样:

const onDelete$ = new Subject();
...
create() {
  return create$
    .takeUntill(onDelete$); 
},
delete() {
  onDelete$.next();
  return ...
}