而不是使用.takeUntil()
,它听起来像你想使用.race()
,这是相当合适的命名。无论哪条小溪首先发射,胜利!另一个是取消订阅。
你需要重新调整一些东西来使用它,只要你想。您希望将您立即发出的第一个动作(request.onStart(meta)
)与ajax请求Observable.fromPromise(apiCall(...args))
分开。然后你想直接在ajax和取消之间进行比赛,所以你需要传递action$
ActionsObservable,因为你已经把所有这些都放在了辅助函数中。
https://jsbin.com/suvaka/edit?js,output
function requestSequence(apiCall, args, meta, action$) {
return Observable.of(request.onStart(meta))
.concat(
Observable.fromPromise(apiCall(...args))
.map((payload) => request.onSuccess(payload, meta))
.catch((e) => Observable.of(request.onError(e, meta)))
.race(
action$.ofType(USER.CANCELLED)
.map(() => request.onCancel(meta))
)
);
}
const fetchUserEpic = (action$, store) =>
action$.ofType(USER.FETCH)
.mergeMap(action =>
requestSequence(
userRequest,
[`/api/users/${action.payload}`],
{ activity: USER.FETCH, path: 'user' },
action$
)
);
边注:小心过早抽象,如让那些五花八门的助手。尽管你可能会在某些史诗中重复一些事情,但我发现抽象它可能会使得以后更难以发现,特别是如果它是别人不写代码而不是Rx大师的话。当然,只有您可以知道这个建议是否适用于您和您的代码库。
对我来说,主要的困惑点是你必须传递给requestSequence
的所有论点,这对许多人来说首先遇到它是很难理解的。如果你发现非常常见的你的史诗般做了完全相同的事情并且你想要重复使用,也许抽象整个史诗将更加清晰,并创建下面的API工具,例如userRequest
,你可以独立测试。
(未经测试,基本上是伪代码)
const createApiEpic = options =>
action$ =>
action$.ofType(options.on)
.mergeMap(action =>
Observable.of(request.onStart(meta))
.concat(
options.effect(action.payload)
.map(payload => request.onSuccess(payload, meta))
.catch(e => Observable.of(request.onError(e, meta)))
.race(
action$.ofType(options.cancel)
.map(() => request.onCancel(meta))
)
)
);
const userRequest = id =>
Observable.ajax.getJSON(`/api/users/${id}`);
const fetchUserEpic = createApiEpic({
on: USER.FETCH,
effect: userRequest
cancel: USER.CANCELLED
});
感谢真棒解释! 'createApiEpic'方法似乎更加清洁。 – winkerVSbecks
@winkerVSbecks欢迎您! – jayphelps