Combination operatorlar
Combination operatorlar bir nechta Observable'ni birlashtiradi — parallel HTTP, form field'larni birga kuzatish va bir stream boshqa stream'ning oxirgi qiymatini olish uchun.
combineLatest — istalgan biri o'zgarganda
Nima bu?
combineLatest — bir nechta Observable'ning har biri kamida bir marta emit qilgach, istalgan biri o'zgarganda barcha oxirgi qiymatlarni massiv (yoki ob'ekt) sifatida emit qiladi. Filter formasi (category + price + sort), dashboard widget'lar va bir nechta FormControl'ni birga kuzatish uchun ideal.
Kod misoli
import { combineLatest } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
@Component({ /* ... */ })
export class ProductFilterComponent {
categoryCtrl = new FormControl('all');
minPriceCtrl = new FormControl(0);
sortCtrl = new FormControl('name');
filteredProducts$ = combineLatest([
this.categoryCtrl.valueChanges.pipe(startWith('all')),
this.minPriceCtrl.valueChanges.pipe(startWith(0)),
this.sortCtrl.valueChanges.pipe(startWith('name')),
]).pipe(
map(([category, minPrice, sort]) => ({ category, minPrice, sort })),
switchMap((filters) => this.api.getProducts(filters))
);
}
// Ob'ekt sintaksisi — nomli field'lar
combineLatest({
user: this.auth.user$,
permissions: this.auth.permissions$,
theme: this.settings.theme$,
}).subscribe(({ user, permissions, theme }) => {
this.render(user, permissions, theme);
});
Imtihonda
- combineLatest va forkJoin farqi nima?
- Nima uchun
startWithkerak bo'ladi?
Yodlash uchun
combineLatest = istalgan o'zgarish → barcha oxirgi qiymatlar. startWith — birinchi emit uchun.
forkJoin — barchasi tugaganda (parallel HTTP)
Nima bu?
forkJoin — bir nechta Observable'ni parallel ishga tushiradi va hammasi complete bo'lganda bitta emit qiladi (Promise.all ga o'xshash). Dashboard yuklash, detail page (user + orders + settings) va bir martalik parallel fetch uchun. Bittasi error bo'lsa butun forkJoin error. Faqat complete bo'ladigan stream'lar uchun — infinite stream ishlamaydi.
Kod misoli
import { forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({ /* ... */ })
export class DashboardComponent implements OnInit {
dashboard$ = forkJoin({
user: this.http.get<User>('/api/me'),
stats: this.http.get<Stats>('/api/stats'),
notifications: this.http.get<Notification[]>('/api/notifications'),
});
ngOnInit() {
this.dashboard$.subscribe({
next: ({ user, stats, notifications }) => {
this.user = user;
this.stats = stats;
this.notifications = notifications;
},
error: (err) => this.handleError(err),
});
}
}
// Array sintaksisi
forkJoin([
this.http.get('/api/a'),
this.http.get('/api/b'),
]).pipe(map(([a, b]) => ({ a, b })));
Imtihonda
- forkJoin va combineLatest qachon ishlatiladi?
- forkJoin infinite stream (interval) bilan ishlaydimi?
Yodlash uchun
forkJoin = parallel + hammasi tugaganda (Promise.all). combineLatest = istalgan o'zgarishda.
zip, merge, concat
Nima bu?
zip — har stream'dan navbatma-navbat bitta qiymat oladi (1-chi + 1-chi, 2-chi + 2-chi). Eng qisqa stream tugaganda to'xtaydi. merge — barcha stream'larni bitta oqimga aralashtiradi, kelish tartibida. concat — birinchi stream tugagach ikkinchisini boshlaydi (concatMap ga o'xshash, lekin stream darajasida).
Gotcha: agar bitta stream boshqasidan doim tezroq emit qilsa, zip tez stream'ning qiymatlarini ichki bufferda ushlab turadi (juftini kutadi) — cheksiz tez interval bilan sekin HTTP javobni zip qilish xotira sizib chiqishiga olib kelishi mumkin. merge va concatda esa bittasi error qaytarsa — butun kombinatsiya darhol error bilan tugaydi, qolgan stream'lar (hali complete bo'lmagan bo'lsa ham) unsubscribe qilinadi — bu forkJoin'dagi xatti-harakatga o'xshaydi, lekin combineLatest'dan farqli, chunki combineLatest ham xato kelganda darhol to'xtaydi.
Kod misoli
import { of, interval } from 'rxjs';
import { zip, merge, concat, take, map } from 'rxjs';
// zip — juftlash
zip(of('A', 'B', 'C'), of(1, 2, 3)).subscribe(console.log);
// ['A', 1], ['B', 2], ['C', 3]
// merge — aralashtirish
merge(
of('a', 'b').pipe(delay(100)),
of(1, 2).pipe(delay(50))
).subscribe(console.log); // 1, 2, a, b (vaqt bo'yicha)
// concat — ketma-ket
concat(
this.http.get('/api/step1'),
this.http.get('/api/step2')
).subscribe(); // step1 tugagach step2
Imtihonda
- zip va combineLatest farqi?
- concat va forkJoin farqi?
Yodlash uchun
zip = juft (index bo'yicha). merge = aralashtir. concat = ketma-ket stream.
withLatestFrom
Nima bu?
withLatestFrom — asosiy (trigger) stream emit qilganda, boshqa stream'larning oxirgi qiymatini oladi va juft emit qiladi. Button click + form qiymati, save click + oxirgi filter holati kabi holatlarda — trigger faqat bir stream'dan, kontekst boshqalardan.
Gotcha: agar hamrohlik qiluvchi (masalan, form.valueChanges) stream hali birorta ham qiymat emit qilmagan bo'lsa, withLatestFrom trigger kelganda ham hech narsa emit qilmaydi — shuning uchun yuqoridagi misolda startWith(this.form.value) shart, aks holda birinchi save click "yo'qolib qoladi". Bu — kombinatsiya operatorlarida eng ko'p uchraydigan silent bug.
Kod misoli
import { fromEvent } from 'rxjs';
import { withLatestFrom, map } from 'rxjs/operators';
@Component({
template: `
<form [formGroup]="form">
<input formControlName="email" />
<button #saveBtn type="button">Saqlash</button>
</form>
`,
})
export class ProfileComponent {
form = new FormGroup({
email: new FormControl(''),
name: new FormControl(''),
});
ngAfterViewInit() {
fromEvent(this.saveBtn.nativeElement, 'click')
.pipe(
withLatestFrom(this.form.valueChanges.pipe(startWith(this.form.value))),
map(([, formValue]) => formValue),
exhaustMap((value) => this.api.saveProfile(value))
)
.subscribe();
}
}
// combineLatest vs withLatestFrom:
// combineLatest — har qandasi o'zgarganda emit
// withLatestFrom — faqat click (trigger) bo'lganda, form oxirgi qiymati bilan
Imtihonda
- Save button + form value — combineLatest yoki withLatestFrom?
- withLatestFrom'da trigger stream qaysi?
Yodlash uchun
withLatestFrom = trigger + boshqalarning oxirgi qiymati. Birinchi argument — trigger.
