我有一个源Observable(实际上是一个Subject),当Observable发出一个值时,我需要启动一个值负载,导致另外两个Observable被更新,当负载完成时,取值源并将其与2个“相关”可观察变量的最新值组合,但仅会发出源可观察变量之后的值。因此,如果在源发出时2个从属可观察变量具有值,则我需要它等待2个从属获得更新之后,才发出所有3个可观察变量中的最新值。我尝试过使用withLatestFrom,switchMap和CombineLatest的各种方法,但是没有任何东西可以提供所需的输出:
这将以正确的形状发出值,但在不正确的时间发出,它使用的是SelectExpenseSummary完成其工作以及费用Details $和expenseReceipts $更新之前的值。
this.editExpenseSubject.pipe(
takeWhile(() => this.active),
tap(x => this.userStore.dispatch(new SelectExpenseSummary(x))),
tap(x => this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:tap', x})),
withLatestFrom(
this.expenseDetails$,
this.expenseReceipts$,
)
)
.subscribe(x => {
this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:subscribe', x});
});
这会发出很多次,最后一次具有正确的值,可以,但是发出错误的形状,它缺少从输出中可观察到的源:
this.editExpenseSubject.pipe(
takeWhile(() => this.active),
tap(x => this.userStore.dispatch(new SelectExpenseSummary(x))),
tap(x => this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:tap', x})),
switchMap(() =>
combineLatest(this.expenseDetails$,
this.expenseReceipts$,
)
)
)
.subscribe(x => {
this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:subscribe', x});
});
这具有正确的输出形状,具有所有3个可观察的对象,但是它在订阅中始终不发出任何内容,我猜是因为内部使用了editExpenseSubject:
this.editExpenseSubject.pipe(
takeWhile(() => this.active),
tap(x => this.userStore.dispatch(new SelectExpenseSummary(x))),
tap(x => this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:tap', x})),
switchMap(x =>
combineLatest(
this.editExpenseSubject
, this.expenseDetails$
, this.expenseReceipts$
)
)
)
.subscribe(x => {
this.log.verbose({type: 'ExpensesFeatureComponent:editExpenseSubject:subscribe', x});
});
有人能指出我正确的咒语以得到我想要的东西吗,最好能详细说明原因。
更新以获取更多信息(我认为这可以帮助您完成整个流程):
this.expenseDetails$ = this.expenseStore.pipe(
select(fromExpenses.getExpenseLines)
);
this.expenseReceipts$ = this.expenseStore.pipe(
select(fromExpenses.getExpenseReceipts)
);
export const getExpenseLines = createSelector(StateFeatureSelector, x => x.expenseLines);
export const getExpenseReceipts = createSelector(StateFeatureSelector, x => x.expenseReceipts);
export interface State {
expenseSummaries: Array<IExpenseSummary>;
expenseLines: Array<IExpenseLine>;
expenseReceipts: Array<IExpenseReceipt>;
selectedUser: IDirectReport;
selectedExpenseSummary: IExpenseSummary;
}
@Effect() LoadExpenseLines$ = this.actions$.pipe(
ofType<fromExpense.LoadExpenseLines>(fromExpense.ActionTypes.LoadExpenseLines)
, tap(action => this.log.debug({type: 'ExpenseEffects:LoadExpenseLines$:filtered', action}))
, mergeMap(x =>
this.service.getExpenseLines(x && x.payload)
.pipe(
tap(receipts => this.log.debug({type: 'ExpenseEffects:getExpenseLines', receipts}))
, map(lineItems => new fromExpense.SetExpenseLines(lineItems))
)
)
);
@Effect() LoadExpenseReceipts$ = this.actions$.pipe(
ofType<fromExpense.LoadExpenseReceipts>(fromExpense.ActionTypes.LoadExpenseReceipts)
, tap(action => this.log.debug({type: 'ExpenseEffects:LoadExpenseReceipts$:filtered', action}))
, mergeMap(x =>
this.service.getExpenseReceipts(x && x.payload)
.pipe(
tap(receipts => this.log.debug({type: 'ExpenseEffects:getExpenseReceipts', receipts}))
, map(receipts => new fromExpense.SetExpenseReceipts(receipts))
)
)
);
@Effect() SelectExpenseSummary$ = this.actions$.pipe(
ofType<fromExpense.SelectExpenseSummary>(fromExpense.ActionTypes.SelectExpenseSummary)
, tap(action => this.log.debug({type: 'ExpenseEffects:SelectExpenseSummary$:filtered', action}))
, mergeMap(x =>
[
new fromExpense.LoadExpenseLines(x.payload)
, new fromExpense.LoadExpenseReceipts(x.payload)
]
)
);
export class SelectExpenseSummary implements Action {
readonly type = ActionTypes.SelectExpenseSummary;
constructor(public payload: IExpenseSummary) {}
}
export class LoadExpenseLines implements Action {
readonly type = ActionTypes.LoadExpenseLines;
constructor(public payload: IExpenseSummary) {}
}
export class SetExpenseLines implements Action {
readonly type = ActionTypes.SetExpenseLines;
constructor(public payload: Array<IExpenseLine>) {}
}
export class LoadExpenseReceipts implements Action {
readonly type = ActionTypes.LoadExpenseReceipts;
constructor(public payload: IExpenseSummary) {}
}
export class SetExpenseReceipts implements Action {
readonly type = ActionTypes.SetExpenseReceipts;
constructor(public payload: Array<IExpenseReceipt>) {}
}
export function reducer (state = initialState, action: actions.ActionsUnion): State {
switch (action.type) {
// ... other actions cut
case actions.ActionTypes.SetExpenseLines:
return {
...state,
expenseLines: action.payload && [...action.payload] || []
};
case actions.ActionTypes.SetExpenseReceipts:
return {
...state,
expenseReceipts: action.payload && [...action.payload] || []
};
default:
return state;
}
}
// from the service class used in the effects
getExpenseLines(summary: IExpenseSummary): Observable<Array<IExpenseLine>> {
this.log.debug({type: 'ExpenseService:getExpenseLines', summary, uri: this.detailUri});
if (summary) {
return this.http
.post<Array<IExpenseLine>>(this.detailUri, {ExpenseGroupId: summary.ExpReportNbr})
.pipe(
tap(data => this.log.verbose({ type: 'ExpenseService:getExpenseLines:reponse', data }))
);
} else {
this.log.log({ type: 'ExpenseService:getExpenseLines null summary'});
return of<Array<IExpenseLine>>([])
.pipe(
tap(data => this.log.verbose({ type: 'ExpenseService:getExpenseLines:reponse', data }))
);
}
}
getExpenseReceipts(summary: IExpenseSummary): Observable<Array<IExpenseReceipt>> {
this.log.debug({type: 'ExpenseService:getExpenseReceipts', summary, uri: this.receiptUri});
if (summary) {
return this.http
.post<Array<IExpenseReceipt>>(this.receiptUri, {ExpenseGroupId: summary.ExpReportNbr})
.pipe(
tap(data => this.log.verbose({ type: 'ExpenseService:getExpenseReceipts:reponse', data }))
);
} else {
this.log.log({ type: 'ExpenseService:getExpenseReceipts null summary'});
return of<Array<IExpenseReceipt>>([])
.pipe(
tap(data => this.log.verbose({ type: 'ExpenseService:getExpenseReceipts:reponse', data }))
);
}
}
答案 0 :(得分:2)
我要从@Reactgular的假设出发:
您想要从被摄对象中获取一个发射值,然后从另外两个观测对象中发射它们的最新值。输出为3个项目的数组。
您希望2个内部可观测对象在外部可观测对象发出后获取新数据,并且只希望它们的第一个值。
从那里开始,想到的运算符是skip
:它跳过您要跳过的值的数量,然后继续。
I have made a sandbox让您在实际中看到它:如您所见,两个可观察对象都是从单个来源更新的,并且您仅获得一个事件,这是每个可观察对象的最后一个值。
相关代码:
import { BehaviorSubject, combineLatest, fromEvent } from "rxjs";
import {
delayWhen,
skip,
distinctUntilKeyChanged,
distinctUntilChanged,
skipUntil,
switchMap,
map
} from "rxjs/operators";
const counts = {
source: 0,
first: 0,
second: 0
};
const source$ = new BehaviorSubject("source = " + counts.source);
const first$ = new BehaviorSubject("first = " + counts.first);
const second$ = new BehaviorSubject("second = " + counts.second);
// Allow the source to emit
fromEvent(document.querySelector("button"), "click").subscribe(() => {
counts.source++;
source$.next("source = " + counts.source);
});
// When the source emits, the others should emit new values
source$.subscribe(source => {
counts.first++;
counts.second++;
first$.next("first = " + counts.first);
second$.next("second = " + counts.second);
});
// Final result should be an array of all observables
const final$ = source$.pipe(
switchMap(source =>
first$.pipe(
skip(1),
switchMap(first =>
second$.pipe(
skip(1),
map(second => [source, first, second])
)
)
)
)
);
final$.subscribe(value => console.log(value));
答案 1 :(得分:1)
如果我对您的理解正确......
您想要从被摄对象中获取一个发射值,然后从另外两个观测对象中发射它们的最新值。输出为3个项目的数组。
您希望2个内部可观测对象在外部可观测对象发出后获取新数据,并且只希望它们的第一个值。
INSERT INTO SAMPLE (NAME, AGE, SALARY, CITY)
SELECT NAME, AGE, SALARY, CITY
FROM SAMPLE2
您可以使用switchMap(),以便它调用内部可观察对象的订阅,并使用forkJoin()获取最终值。添加first()运算符以将可观察值限制为1个值。然后,您使用map()将外部值放回到结果数组中。