当前位置: 移动技术网 > IT编程>脚本编程>AngularJs > 浅谈Angular 中何时取消订阅

浅谈Angular 中何时取消订阅

2017年12月08日  | 移动技术网IT编程  | 我要评论

你可能知道当你订阅 observable 对象或设置事件监听时,在某个时间点,你需要执行取消订阅操作,进而释放操作系统的内存。否则,你的应用程序可能会出现内存泄露。

接下来让我们看一下,需要在 ngondestroy 生命周期钩子中,手动执行取消订阅操作的一些常见场景。

手动释放资源场景

表单

export class testcomponent {

 ngoninit() {
  this.form = new formgroup({...});
  // 监听表单值的变化
  this.valuechanges = this.form.valuechanges.subscribe(console.log);
  // 监听表单状态的变化              
  this.statuschanges = this.form.statuschanges.subscribe(console.log);
 }

 ngondestroy() {
  this.valuechanges.unsubscribe();
  this.statuschanges.unsubscribe();
 }
}

以上方案也适用于其它的表单控件。

路由

export class testcomponent {
 constructor(private route: activatedroute, private router: router) { }

 ngoninit() {
  this.route.params.subscribe(console.log);
  this.route.queryparams.subscribe(console.log);
  this.route.fragment.subscribe(console.log);
  this.route.data.subscribe(console.log);
  this.route.url.subscribe(console.log);
  
  this.router.events.subscribe(console.log);
 }

 ngondestroy() {
  // 手动执行取消订阅的操作
 }
}

renderer 服务

export class testcomponent {
 constructor(
  private renderer: renderer2, 
  private element : elementref) { }

 ngoninit() {
  this.click = this.renderer
    .listen(this.element.nativeelement, "click", handler);
 }

 ngondestroy() {
  this.click.unsubscribe();
 }
}

infinite observables

当你使用 interval() 或 fromevent() 操作符时,你创建的是一个无限的 observable 对象。这样的话,当我们不再需要使用它们的时候,就需要取消订阅,手动释放资源。

export class testcomponent {
 constructor(private element : elementref) { }

 interval: subscription;
 click: subscription;

 ngoninit() {
  this.interval = observable.interval(1000).subscribe(console.log);
  this.click = observable.fromevent(this.element.nativeelement, 'click')
              .subscribe(console.log);
 }

 ngondestroy() {
  this.interval.unsubscribe();
  this.click.unsubscribe();
 }
}

redux store

export class testcomponent {

 constructor(private store: store) { }

 todos: subscription;

 ngoninit() {
   /**
   * select(key : string) {
   *  return this.map(state => state[key]).distinctuntilchanged();
   * }
   */
   this.todos = this.store.select('todos').subscribe(console.log); 
 }

 ngondestroy() {
  this.todos.unsubscribe();
 }
}

无需手动释放资源场景

asyncpipe

@component({
 selector: 'test',
 template: `<todos [todos]="todos$ | async"></todos>`
})
export class testcomponent {
 constructor(private store: store) { }
 
 ngoninit() {
   this.todos$ = this.store.select('todos');
 }
}

当组件销毁时,async 管道会自动执行取消订阅操作,进而避免内存泄露的风险。

angular asyncpipe 源码片段

@pipe({name: 'async', pure: false})
export class asyncpipe implements ondestroy, pipetransform {
 // ...
 constructor(private _ref: changedetectorref) {}

 ngondestroy(): void {
  if (this._subscription) {
   this._dispose();
  }
 }
}

@hostlistener

export class testdirective {
 @hostlistener('click')
 onclick() {
  ....
 }
}

需要注意的是,如果使用 @hostlistener 装饰器,添加事件监听时,我们无法手动取消订阅。如果需要手动移除事件监听的话,可以使用以下的方式:

// subscribe
this.handler = this.renderer.listen('document', "click", event =>{...});

// unsubscribe
this.handler();

finite observable

当你使用 http 服务或 timer observable 对象时,你也不需要手动执行取消订阅操作。

export class testcomponent {
 constructor(private http: http) { }

 ngoninit() {
  // 表示1s后发出值,然后就结束了
  observable.timer(1000).subscribe(console.log);
  this.http.get('http://api.com').subscribe(console.log);
 }
}

操作符签名

复制代码 代码如下:

public static timer(initialdelay: number | date, period: number, scheduler: scheduler): observable

操作符作用

timer 返回一个发出无限自增数列的 observable,具有一定的时间间隔,这个间隔由你来选择。

操作符示例

// 每隔1秒发出自增的数字,3秒后开始发送
var numbers = rx.observable.timer(3000, 1000);
numbers.subscribe(x => console.log(x));

// 5秒后发出一个数字
var numbers = rx.observable.timer(5000);
numbers.subscribe(x => console.log(x));

最终建议

你应该尽可能少的调用 unsubscribe() 方法,你可以在rxjs: don't unsubscribe 这篇文章中了解与 subject 相关更多信息。

具体示例如下:

export class testcomponent {
 constructor(private store: store) { }

 private componetdestroyed: subject = new subject();
 todos: subscription;
 posts: subscription;

 ngoninit() {
   this.todos = this.store.select('todos')
           .takeuntil(this.componetdestroyed).subscribe(console.log); 
           
   this.posts = this.store.select('posts')
           .takeuntil(this.componetdestroyed).subscribe(console.log); 
 }

 ngondestroy() {
  this.componetdestroyed.next();
  this.componetdestroyed.unsubscribe();
 }
}

操作符签名

public takeuntil(notifier: observable): observable<t>

操作符作用

发出源 observable 发出的值,直到 notifier observable 发出值。

操作符示例

var interval = rx.observable.interval(1000);
var clicks = rx.observable.fromevent(document, 'click');
var result = interval.takeuntil(clicks);

result.subscribe(x => console.log(x));

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网