У меня есть 2 лица: получатель платежа и транзакция. У меня есть payeeCreateEpic
что фильтры при started
испускает обычную цепочку finished
/failed
. То же самое transactionCreateEpic
.
Для простоты предположим, что Получатель платежа имеет только UUID и Имя. Транзакция, однако, может иметь одну из 3-х возможностей: не привязана ни к одному получателю (полезная нагрузка имеет payee: null
), присоединена к существующему получателю (в пользовательском интерфейсе у пользователя есть выпадающий список всех получателей, где он выбирает одного, полезная нагрузка, имеющая payee: payee-uuid
) или присоединен к несуществующему получателю (в пользовательском интерфейсе у пользователя есть возможность создать получателя, который не сохраняется в бэкэнде, но должен быть сохранен, если вся транзакция сохраняется, в полезной нагрузке, которую я отправляю payee: payee-name
),
Теперь, если создать сделки представляется, не получателем платежа или существующего получателя платежа, я просто иду в POST/transactions
и ждать его, чтобы решить, чтобы испускать finished
или failed
действие transactionCreateEpic
.
Однако, если !isUuid(txPayload.payee)
оценивается как true, в эпопее transactionCreateEpic
payeeCreateEpic
я хочу инициировать payeeCreateEpic
с именем получателя платежа и ждать finished
=>, а затем создать транзакцию с идентификатором получателя из серверной части или произошел failed
=> прервать транзакцию (и показать ошибку для пользователя).
Я не уверен, как это сделать, поскольку в эпосе транзакции я уже нахожусь внутри фильтра для действия TRANSACTION_CREATE_STARTED
и не уверен, как я могу подписаться на другое действие, созданное payeeCreateEpic
.
Некоторый код:
const createPayeeEpic = (actions$: Observable<Action>) =>
actions$.pipe(
filter(CreateAction.start.match),
mergeMap((action) =>
from(MoneyPinApiClient.getInstance().payee.create(CreateRequestAdapter(action.payload))).pipe(
map((response) => CreateAction.success({
params: action.payload,
result: CreateResultAdapter(response.data)
})),
catchError((err) => of(
<any>CreateAction.failure({params: action.payload, error: err}),
<any>MoneyPinApiErrorAction(err)
))
)
)
);
const createTransactionEpic = (actions$: Observable<Action>) =>
actions$.pipe(
filter(CreateAction.start.match),
mergeMap((action) => {
if(!isUuid(action.payload.payee) {
**EMIT PayeeCreateAction.start({name: action.payload.payee})**
**WAIT FOR PayeeCreateAction.success (or PayeeCreateAction.failure)**
action.payload.payee = resultOf(PayeeCreateAction.success).id;
}
return from(MoneyPinApiClient.getInstance().transaction.create(CreateRequestAdapter(action.payload))).pipe(
map((response) => CreateAction.success({
params: action.payload,
result: CreateResultAdapter(response.data)
})),
catchError((err) => of(
<any>CreateAction.failure({params: action.payload, error: err}),
<any>MoneyPinApiErrorAction(err)
))
)
})
);
Посмотрите на createTransactionEpic
, вот где я с трудом могу инициировать createPayeeEpic + ожидание успешного сбоя.
Решение состоит в том, чтобы извлечь фактическое создание транзакции в некоторый фабричный метод:
const transactionCreateStreamFactory = (action: MoneyPinAction<CreateActionParams>) =>
from(MoneyPinApiClient.getInstance().transaction.create(CreateRequestAdapter(action.payload))).pipe(
concatMap((response) => CreateAction.success({
params: action.payload,
result: CreateResultAdapter(response.data)
})),
catchError((err) => of(
<any>CreateAction.failure({params: action.payload, error: err}),
<any>MoneyPinApiErrorAction(err)
))
);
И тогда эпопея создания транзакции на самом деле выглядит так:
const createTransactionEpic = (actions$: Observable<Action>) =>
actions$.pipe(
filter(CreateAction.start.match),
mergeMap((action) => {
const {payee, payload} = action.payload;
if (payee && payload.payee_id) {
return merge(
actions$.pipe(
filter(PayeeCreateAction.success.match),
filter((payeeResponse) => payeeResponse.payload.result.payee.id === payload.payee_id),
take(1),
mergeMap(() => transactionCreateStreamFactory(action)),
),
actions$.pipe(
filter(PayeeCreateAction.failure.match),
filter((payeeResponse) => payeeResponse.payload.params.payload.id === payload.payee_id),
take(1),
map(() => CreateAction.failure({
params: action.payload,
error: new Error("failed to create payee")
}))
)
).pipe(
startWith(PayeeCreateAction.start({
notebook_id: action.payload.notebook_id,
payload: {id: payload.payee_id, name: payee}
}))
);
} else {
return transactionCreateStreamFactory(action);
}
})
);
Поэтому, если получатель платежа с идентификатором был задан во время создания, я бы запустил другой канал, который прослушивает действие payee.success
или payee.failure
, фильтрует соответствующее действие (в случае, если во время одновременного создания может произойти несколько действий этого типа или что-то еще), прослушивает только один раз (take(1)
) и объединяет это с фактическим эпосом создания транзакции, и весь поток начинается с действия payee.start
, которое обрабатывается эпосом получателя (и, в свою payee.start
, вызывает успех/неудачу действия получателя).
Ваша проблема выглядит как синхронизация потоков. Используйте один из шаблонов синхронизации потоков. Например, https://en.wikipedia.org/wiki/Monitor_(synchronization)