而不是 using .takeUntil()
,听起来你想使用.race()
,它的名字相当贴切。无论哪个流先发出,获胜!另一个已取消订阅。
您需要稍微重组一些东西才能根据需要使用它。您想将您立即发出的第一个操作,您的request.onStart(meta)
,与 ajax 请求分开Observable.fromPromise(apiCall(...args))
。然后,您想直接在该 ajax 和取消之间进行竞争,因此您需要传入action$
ActionsObservable ,因为您将所有这些都放在帮助程序中。
https://jsbin.com/suvaka/edit?js,输出
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
,当他们第一次遇到它时,许多人很难理解。如果您发现您的史诗非常常见地做完全相同的事情并且您想要重用,那么抽象整个史诗可能会更清晰,并创建userRequest
如下所示的 API 实用程序,您可以独立测试。
(未经测试,基本上是伪代码)
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
});