用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill rxjs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | rxjs |
| description | RxJS patterns for Angular |
| allowed-tools | [] |
Ben Lesh's core belief: Observables are for events over time, not single values. Use the right tool: Promises for single async values, Observables for streams.
"RxJS is a library for composing asynchronous and event-based programs using observable sequences."
The key word is sequences. If you have one value, you probably don't need RxJS.
RxJS adds complexity. Use it when you have:
Don't need RxJS:
// Single HTTP call - Promise is fine
async getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
Need RxJS:
// Combining multiple sources, need cancellation
searchResults$ = this.searchTerm$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.searchService.search(term))
);
switchMap, mergeMap, concatMap, exhaustMap - know the difference.
| Operator | Behavior | Use When |
|---|---|---|
switchMap | Cancels previous | Type-ahead search |
mergeMap | Runs all in parallel | Independent requests |
concatMap | Queues, runs in order | Order matters |
exhaustMap | Ignores new until done | Prevent double-submit |
Not this:
// Nested subscribes - callback hell returns
this.searchTerm$.subscribe(term => {
this.searchService.search(term).subscribe(results => {
this.results = results;
});
});
This:
// Flat, cancels previous search
this.results$ = this.searchTerm$.pipe(
debounceTime(300),
switchMap(term => this.searchService.search(term))
);
If you're subscribing inside a subscribe, you're doing it wrong.
Not this:
this.user$.subscribe(user => {
this.ordersService.getOrders(user.id).subscribe(orders => {
this.orders = orders;
});
});
This:
this.orders$ = this.user$.pipe(
switchMap(user => this.ordersService.getOrders(user.id))
);
Subjects are escape hatches. Prefer declarative streams.
Not this:
class UserService {
private usersSubject = new BehaviorSubject<User[]>([]);
users$ = this.usersSubject.asObservable();
loadUsers() {
this.http.get<User[]>('/users').subscribe(users => {
this.usersSubject.next(users); // Imperative push
});
}
}
This:
class UserService {
private refresh$ = new Subject<void>();
users$ = this.refresh$.pipe(
startWith(undefined),
switchMap(() => this.http.get<User[]>('/users')),
shareReplay(1)
);
refresh() {
this.refresh$.next();
}
}
Multiple subscribers shouldn't trigger multiple HTTP calls.
Not this:
// Each async pipe triggers a new HTTP request
user$ = this.http.get<User>('/api/user');
<div>{{ (user$ | async)?.name }}</div>
<div>{{ (user$ | async)?.email }}</div>
<!-- Two HTTP requests! -->
This:
user$ = this.http.get<User>('/api/user').pipe(
shareReplay(1)
);
<div>{{ (user$ | async)?.name }}</div>
<div>{{ (user$ | async)?.email }}</div>
<!-- One HTTP request, shared -->
shareReplay options:
shareReplay({ bufferSize: 1, refCount: true })
// refCount: true = cleanup when no subscribers
// refCount: false = keep cached value forever
Errors terminate streams. Catch and recover.
Not this:
// Error terminates stream - no more searches work
results$ = searchTerm$.pipe(
switchMap(term => this.searchService.search(term))
// Error here kills the whole stream
);
This:
results$ = searchTerm$.pipe(
switchMap(term => this.searchService.search(term).pipe(
catchError(error => {
console.error('Search failed:', error);
return of([]); // Recover with empty results
})
))
);
Memory leaks from forgotten subscriptions are the #1 RxJS bug.
Options (best to worst):
// 1. BEST: async pipe (auto-unsubscribes)
@Component({
template: `<div *ngFor="let item of items$ | async">{{ item }}</div>`
})
// 2. GOOD: takeUntilDestroyed (Angular 16+)
@Component({})
class MyComponent {
items$ = this.service.getItems().pipe(
takeUntilDestroyed()
);
}
// 3. OK: takeUntil with destroy subject
@Component({})
class MyComponent implements OnDestroy {
private destroy$ = new Subject<void>();
ngOnInit() {
this.service.getItems().pipe(
takeUntil(this.destroy$)
).subscribe(items => this.items = items);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
// 4. AVOID: Manual unsubscribe (easy to forget)
Signals are simpler than BehaviorSubject for synchronous state.
Before (RxJS for everything):
class CounterService {
private countSubject = new BehaviorSubject(0);
count$ = this.countSubject.asObservable();
increment() {
this.countSubject.next(this.countSubject.value + 1);
}
}
After (Signals for sync state):
class CounterService {
count = signal(0);
doubleCount = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
}
Rule: Use Signals for synchronous state, Observables for async streams.
Use combination operators, not imperative code.
// Combine latest values
vm$ = combineLatest([
this.user$,
this.permissions$,
this.settings$
]).pipe(
map(([user, permissions, settings]) => ({ user, permissions, settings }))
);
// Wait for all to complete
allData$ = forkJoin([
this.usersService.getAll(),
this.rolesService.getAll()
]);
// Race - first to emit wins
result$ = race([
this.cache.get(key),
this.api.get(key)
]);
results$ = searchTerm$.pipe(
tap(term => console.log('Search term:', term)),
switchMap(term => this.searchService.search(term)),
tap(results => console.log('Results:', results.length))
);
Before using RxJS, ask:
Apply these checks:
// Type-ahead search
search$ = this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
filter(term => term.length >= 2),
switchMap(term => this.searchService.search(term).pipe(
catchError(() => of([]))
))
);
// Polling
data$ = timer(0, 30000).pipe(
switchMap(() => this.api.getData()),
retry(3),
shareReplay(1)
);
// Optimistic update
save(item: Item) {
const optimistic$ = of(item); // Immediate
const server$ = this.api.save(item).pipe(delay(0));
(optimistic$, server$);
}
Use a different skill when:
angular-coreangular-perfBen Lesh is the RxJS/reactive skill—use it for streams, events, and complex async composition.
"RxJS is powerful, but with great power comes great responsibility. Don't use it for everything." — Ben Lesh