Khi nào tôi nên tạo Đăng ký mới cho một hiệu ứng phụ cụ thể?


10

Tuần trước tôi đã trả lời một câu hỏi của RxJS khi tôi tham gia một cuộc thảo luận với một thành viên khác trong cộng đồng về: "Tôi nên tạo một thuê bao cho mỗi tác dụng phụ cụ thể hay tôi nên cố gắng giảm thiểu đăng ký nói chung?" Tôi muốn biết sử dụng phương pháp đo lường nào theo cách tiếp cận ứng dụng phản ứng đầy đủ hoặc khi nào nên chuyển đổi từ phương pháp này sang phương pháp khác. Điều này sẽ giúp tôi và có thể những người khác để tránh các cuộc thảo luận không liên quan.

Thông tin cài đặt

  • Tất cả các ví dụ đều có trong TypeScript
  • Để tập trung tốt hơn vào câu hỏi không sử dụng vòng đời / nhà xây dựng cho đăng ký và để giữ trong khuôn khổ không liên quan
    • Hãy tưởng tượng: Đăng ký được thêm vào trong constructor / vòng đời init
    • Hãy tưởng tượng: Hủy đăng ký được thực hiện trong vòng đời hủy

Tác dụng phụ là gì (mẫu góc)

  • Cập nhật / Nhập liệu trong UI (ví dụ value$ | async)
  • Đầu ra / ngược dòng của một thành phần (ví dụ @Output event = event$)
  • Tương tác giữa các dịch vụ khác nhau trên các hệ thống phân cấp khác nhau

Usecase mẫu mực:

  • Hai chức năng: foo: () => void; bar: (arg: any) => void
  • Hai nguồn quan sát: http$: Observable<any>; click$: Observable<void>
  • foođược gọi sau khi http$đã phát ra và không cần giá trị
  • barđược gọi sau khi click$phát ra, nhưng cần giá trị hiện tại củahttp$

Case: Tạo một thuê bao cho mỗi tác dụng phụ cụ thể

const foo$ = http$.pipe(
  mapTo(void 0)
);

const bar$ = http$.pipe(
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue)
  )
);

foo$.subscribe(foo);
bar$.subscribe(bar);

Trường hợp: Giảm thiểu đăng ký nói chung

http$.pipe(
  tap(() => foo()),
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue )
  )
).subscribe(bar);

Tóm lại ý kiến ​​của riêng tôi

Tôi có thể hiểu thực tế rằng đăng ký làm cho cảnh quan Rx phức tạp hơn lúc đầu, bởi vì bạn phải suy nghĩ về việc người đăng ký có ảnh hưởng đến đường ống hay không (ví dụ như bạn có thể quan sát được hay không). Nhưng bạn càng tách mã của mình (bạn càng tập trung: điều gì xảy ra khi nào) thì càng dễ duy trì (kiểm tra, gỡ lỗi, cập nhật) mã của bạn trong tương lai. Với ý nghĩ đó, tôi luôn tạo một nguồn có thể quan sát được và một thuê bao duy nhất cho bất kỳ tác dụng phụ nào trong mã của tôi. Nếu hai hoặc nhiều tác dụng phụ tôi đã được kích hoạt bởi cùng một nguồn chính xác có thể quan sát được, thì tôi chia sẻ quan sát của mình và đăng ký cho từng tác dụng phụ riêng lẻ, bởi vì nó có thể có vòng đời khác nhau.

Câu trả lời:


6

RxJS là một tài nguyên có giá trị để quản lý các hoạt động không đồng bộ và nên được sử dụng để đơn giản hóa mã của bạn (bao gồm giảm số lượng đăng ký) nếu có thể. Tương tự, không thể tự động theo dõi đăng ký theo dõi có thể quan sát được, nếu RxJS cung cấp giải pháp có thể giảm tổng số đăng ký trong ứng dụng của bạn.

Tuy nhiên, có những tình huống có thể có ích khi tạo đăng ký không hoàn toàn 'cần thiết':

Một ngoại lệ ví dụ - tái sử dụng các vật quan sát trong một mẫu duy nhất

Nhìn vào ví dụ đầu tiên của bạn:

// Component:

this.value$ = this.store$.pipe(select(selectValue));

// Template:

<div>{{value$ | async}}</div>

Nếu giá trị $ chỉ được sử dụng một lần trong một mẫu, tôi sẽ tận dụng lợi thế của ống không đồng bộ và lợi ích của nó để tiết kiệm mã và hủy đăng ký tự động. Tuy nhiên, theo câu trả lời này , nên tránh nhiều tham chiếu đến cùng một biến async trong một mẫu, ví dụ:

// It works, but don't do this...

<ul *ngIf="value$ | async">
    <li *ngFor="let val of value$ | async">{{val}}</li>
</ul>

Trong tình huống này, thay vào đó tôi sẽ tạo một thuê bao riêng và sử dụng điều này để cập nhật biến không đồng bộ trong thành phần của mình:

// Component

valueSub: Subscription;
value: number[];

ngOnInit() {
    this.valueSub = this.store$.pipe(select(selectValue)).subscribe(response => this.value = response);
}

ngOnDestroy() {
    this.valueSub.unsubscribe();
}

// Template

<ul *ngIf="value">
    <li *ngFor="let val of value">{{val}}</li>
</ul>

Về mặt kỹ thuật, có thể đạt được kết quả tương tự mà không cần valueSub, nhưng các yêu cầu của ứng dụng có nghĩa đây là lựa chọn đúng đắn.

Xem xét vai trò và tuổi thọ của một quan sát trước khi quyết định có đăng ký hay không

Nếu hai hoặc nhiều vật quan sát chỉ được sử dụng khi được sử dụng cùng nhau, các toán tử RxJS thích hợp nên được sử dụng để kết hợp chúng thành một thuê bao duy nhất.

Tương tự, nếu First () đang được sử dụng để lọc tất cả trừ phát xạ đầu tiên có thể quan sát được, tôi nghĩ có lý do lớn hơn để tiết kiệm mã của bạn và tránh đăng ký 'thêm', so với việc có thể quan sát được có vai trò liên tục trong phiên.

Khi bất kỳ vật quan sát riêng lẻ nào hữu ích độc lập với các vật thể khác, tính linh hoạt và rõ ràng của (các) thuê bao riêng biệt có thể đáng xem xét. Nhưng theo tuyên bố ban đầu của tôi, một thuê bao không nên được tạo tự động cho mọi quan sát, trừ khi có một lý do rõ ràng để làm như vậy.

Về thông tin đăng ký:

Một điểm chống lại đăng ký bổ sung là yêu cầu nhiều hơn không đăng ký . Như bạn đã nói, chúng tôi muốn giả định rằng tất cả các mô tả không cần thiết được áp dụng trênDestroy, nhưng cuộc sống thực không phải lúc nào cũng diễn ra suôn sẻ như vậy! Một lần nữa, RxJS cung cấp các công cụ hữu ích (ví dụ đầu tiên () ) để hợp lý hóa quy trình này, giúp đơn giản hóa mã và giảm nguy cơ rò rỉ bộ nhớ. Bài viết này cung cấp thêm thông tin và ví dụ liên quan, có thể có giá trị.

Sở thích cá nhân / tính dài dòng so với sự căng thẳng:

Đừng có sở thích của riêng bạn vào tài khoản. Tôi không muốn đi đến một cuộc thảo luận chung về tính dài dòng của mã, nhưng mục đích là để tìm sự cân bằng phù hợp giữa quá nhiều 'tiếng ồn' và làm cho mã của bạn trở nên quá khó hiểu. Điều này có thể đáng xem .


Đầu tiên cảm ơn bạn đã trả lời chi tiết của bạn! Về quan điểm số 1: theo quan điểm của tôi, ống không đồng bộ cũng là một đăng ký / hiệu ứng phụ, chỉ là nó được che giấu trong một chỉ thị. Về số 2, bạn có thể vui lòng thêm một số mẫu mã không, tôi không hiểu chính xác những gì bạn đang nói với tôi. Về việc hủy đăng ký: Tôi chưa bao giờ có nó trước khi đăng ký cần phải hủy đăng ký ở bất kỳ nơi nào khác ngoài ngOnDestroy. Theo mặc định, First () không thực sự quản lý việc hủy đăng ký cho bạn: 0 emits = đăng ký mở, mặc dù thành phần bị hủy.
Jonathan Stellwag

1
Điểm 2 thực sự chỉ là xem xét vai trò của từng người có thể quan sát được khi quyết định có nên thiết lập đăng ký hay không, thay vì tự động theo dõi mọi quan sát có đăng ký. Về điểm này, tôi khuyên bạn nên xem xét Medium.com/@ben Meat/rxjs-dont-unsubscribe-6753ed4fda87 , trong đó nói: "Giữ quá nhiều đối tượng đăng ký xung quanh là dấu hiệu bạn đang quản lý đăng ký của mình một cách bắt buộc và không tận dụng lợi thế sức mạnh của Rx. "
Matt Saunders

1
Cảm ơn cảm ơn @JonathanStellwag. Cũng cảm ơn về thông tin cuộc gọi lại RE - trong ứng dụng của bạn, bạn có hủy đăng ký rõ ràng từ mọi đăng ký (ngay cả khi lần đầu tiên () được sử dụng), để ngăn chặn điều này không?
Matt Saunders

1
Vâng tôi đồng ý. Trong dự án hiện tại, chúng tôi đã giảm thiểu khoảng 30% tất cả các nút treo xung quanh chỉ bằng cách hủy đăng ký luôn.
Jonathan Stellwag

2
Sở thích hủy đăng ký của tôi được takeUntilkết hợp với một chức năng được gọi từ ngOnDestroy. Đó là một lớp lót bổ sung điều này vào đường ống : takeUntil(componentDestroyed(this)). stackoverflow.com/a/60223749/5367916
Kurt Hamilton

2

Nếu tối ưu hóa đăng ký là kết thúc của bạn, tại sao không đi đến cực kỳ logic và chỉ theo mô hình chung này:

 const obs1$ = src1$.pipe(tap(effect1))
 const obs2$ = src2$pipe(tap(effect2))
 merge(obs1$, obs2$).subscribe()

Hoàn toàn thực hiện các tác dụng phụ trong tap và kích hoạt với hợp nhất có nghĩa là bạn chỉ có một thuê bao.

Một lý do để không làm điều này là bạn đang trung lập nhiều thứ khiến RxJS trở nên hữu ích. Đó là khả năng soạn các luồng có thể quan sát và đăng ký / hủy đăng ký từ các luồng theo yêu cầu.

Tôi sẽ lập luận rằng các thiết bị quan sát của bạn nên được sáng tác một cách hợp lý và không bị ô nhiễm hoặc nhầm lẫn trong tên của việc giảm đăng ký. Hiệu ứng foo có nên được kết hợp một cách hợp lý với hiệu ứng thanh không? Người này có cần người kia không? Tôi có bao giờ có thể không muốn kích hoạt foo khi http $ phát ra không? Tôi có tạo ra khớp nối không cần thiết giữa các chức năng không liên quan? Đây là tất cả các lý do để tránh đưa chúng vào một luồng.

Đây là tất cả thậm chí không xem xét xử lý lỗi dễ quản lý hơn với nhiều đăng ký IMO


Cảm ơn bạn vì câu trả lời. Tôi xin lỗi vì tôi chỉ có thể chấp nhận một câu trả lời. Câu trả lời của bạn giống với câu trả lời của @Matt Saunders. Nó chỉ là một quan điểm khác. Vì nỗ lực của Matt, tôi đã cho anh ấy chấp nhận. Tôi hy vọng bạn có thể tha thứ cho tôi :)
Jonathan Stellwag
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.