ngrx 效果:由一个效果调度的动作是不是立即被其他效果处理?

Posted

技术标签:

【中文标题】ngrx 效果:由一个效果调度的动作是不是立即被其他效果处理?【英文标题】:ngrx Effects: Are actions dispatched by one effect processed immediately by other effects?ngrx 效果:由一个效果调度的动作是否立即被其他效果处理? 【发布时间】:2017-08-01 14:21:27 【问题描述】:

我有一个具有四个 ngrx 操作的 Angular (2) 应用程序:

开始 reducer 未处理(无状态更改) ngrx Effect 调用异步任务并映射到 SUCCESS 或 ERROR 成功 由reducer处理 ngrx 效果映射到 ADVANCE 高级 reducer 未处理 ngrx 效果导航到不同的路线 错误 由reducer处理 没有效果

问题在于,捕获 ADVANCE 的 Effect 似乎在处理 SUCCESS 的 reducer 之前运行

这是效果代码:

@Effect() start$ = this.actions$
    .ofType('START')
    .map(toPayload)
    .switchMap(input => doAsyncTask(input)
        .map(result => (type: 'SUCCESS', payload: result))
        .catch(error => (type: 'ERROR', payload: error)));

@Effect() success$ = this.actions$
    .ofType('SUCCESS')
    .map(() => (type: 'ADVANCE'));

@Effect(dispatch: false) advance$ = this.actions$
    .ofType('ADVANCE')
    .withLatestFrom(this.store$.select(state => state.route))
    .map(action_route => action_route[1])
    .do(route => this.router.navigate([route.foo.bar]));

我得到的错误是Cannot read property 'bar' of nullfoo 属性由处理 SUCCESS 的 reducer 设置。

如果我为 SUCCESS 效果添加延迟,一切都会很好:

@Effect() success$ = this.actions$
    .ofType('SUCCESS')
    .delay(1)
    .map(() => (type: 'ADVANCE'));

但必须添加此延迟对我来说没有意义。

我在各处添加了console.log 语句,输出如下所示:

    成功效应 ADVANCE 效果(显示 route.foo === null) SUCCESS reducer(显示 route.foo === 某些东西) 错误

我预计 SUCCESS 效果和 SUCCESS reducer 在 ADVANCE 效果之前运行。

我做错了吗?

期望reducer 以与调度相同的顺序处理动作是不正确的吗?


版本:

@angular/cli: 1.0.0-beta.32.3 节点:7.5.0 操作系统:darwin x64 @angular/common: 2.4.7 @angular/编译器:2.4.7 @angular/core: 2.4.7 @angular/forms: 2.4.7 @angular/http:2.4.7 @angular/platform-b​​rowser: 2.4.7 @angular/platform-b​​rowser-dynamic: 2.4.7 @angular/路由器:3.4.7 @angular/cli: 1.0.0-beta.32.3 @angular/compiler-cli: 2.4.7 @ngrx/core@1.2.0 @ngrx/effects@2.0.0 @ngrx/store@2.2.1 rxjs:5.1.1

【问题讨论】:

您使用的是什么版本?通过类似的安排,我看到减速器在效果之前处理动作。 @cartant - 我将版本添加到问题中。感谢您的检查。 我的@ngrx 版本是一样的。你的 RxJS 版本呢?我的是5.2.0。您可能还想在问题中包含您的 NgModule 引导程序。 store(通过reducer manager)和observable的效果动作都订阅了dispatcher。听起来您的效果以某种方式在商店之前订阅了调度程序。 @cartant - 我有 rxjs 5.1.1。我昨天花了几个小时调试这个但没有得到任何结果。我今天会多花一些。 【参考方案1】:

回答你的结论性问题:

我做错了吗?
不,我不认为你做错了什么。
期望reducer 以与分派相同的顺序处理操作是否不正确?
这个顺序看起来合乎逻辑,但显然你现在不能指望它

似乎有一个错误导致了这种行为。您很可能正在经历它,因为您直接映射到另一个动作。通常,如果你有一个异步操作或类似的操作,reducer 有时间在监听下一个动作的效果开始之前完成。

也许不是您问题的一部分,但您指定问题的解决方案是在有效负载的帮助下直接在成功中导航到您的新路线,或将有效负载传递给 ADVANCE。

以下链接是与您的相关的效果项目、商店项目和 rxjs 项目中报告的问题:

https://github.com/ngrx/effects/issues/58 https://github.com/ngrx/store/issues/279 /ReactiveX/rxjs/issues/2155(声誉太低,无法链接到这个)

他们似乎正在努力:)

【讨论】:

【参考方案2】:

用 .concatMap 替换 START 操作的捕获中的 .map 并将您的地图放到 SUCCESS 和 ACTION 那里可能会起作用。

@Effect() start$ = this.actions$
    .ofType('START')
    .map(toPayload)
    .switchMap(input => doAsyncTask(input)
        .concatMap(result => 
            return Observable.from([type: 'SUCCESS', payload: result,
                                    type: 'ADVANCE'])
        .catch(error => (type: 'ERROR', payload: error)));

【讨论】:

【参考方案3】:

ngex/effects 的目的是在基于 redux 的环境中充当中间件。

所以你在这里基本上做的是调用“成功”动作,它应该在减速器上工作并继续另一个动作是中间件。但这就是 effecs 的辅音,它应该拦截动作并自己处理,只有在异步处理之后才调度动作。

那么这里发生了什么?您将成功发送到两个不同的地方,一个应该由减速器处理,一个由中间件处理。我不确定它是否总是这样,但中间件会在到达减速器之前拦截传入的动作。我认为,如果您稍微修改一下代码,它应该可以工作。尝试做类似的事情:

@Effect() start$ = this.actions$
    .ofType('START')
    .map(toPayload)
    .switchMap(input => doAsyncTask(input)
        .map(result =>
               this.store$.dispatch(
               type: 'SUCCESS',
                payload: result
                );
               this.store$.dispatch(
               type: 'ADVANCE')



        .catch(error => (type: 'ERROR', payload: error)));

你看到我在这里做什么了吗?我已经删除了拦截“成功”的中间件,所以“成功”不会击中减速器。在它发送另一个类型为“advance”的操作之后,该操作应该在“成功”处理完毕后触发您的中间件。

希望对您有所帮助。

【讨论】:

谢谢吉利。我不确定在效果内调用this.store$.dispatch。我认为它没有任何根本性的错误,但我认为它应该是不必要的,因为效果能够将一个动作映射到另一个动作。现在,您确定 ngrx 效果会在动作到达减速器之前拦截它们吗? 这是redux官方关于middle-wares的文档 -redux.js.org/docs/advanced/Middleware.html 。这是我在“它在调度一个动作和它到达减速器的那一刻之间提供了一个第三方扩展点”的引用。我在我的项目中也使用了redux和effects,我根据我的理解和经验来回答。我不将两个动作映射为一个的原因是我希望完全控制它们的处理顺序。 无需在效果内调用store.dispatch。在@ngrx/effects 中,store 是subscribed to the actions emitted by the effects,store 是next is an alias for dispatch,所以调用store.dispatch 与发出actions 没有区别。 OP 的问题很可能与配置有关;似乎商店正在订阅动作主题效果之后 - 调用 store.dispatch 不会有任何区别。 虽然 Effects 类似于 redux 中间件,但它们在 reducer 运行后触发,而不是之前。你不能长时间使用 Redux 文档,因为 NGRX 与 Redux 相差甚远。

以上是关于ngrx 效果:由一个效果调度的动作是不是立即被其他效果处理?的主要内容,如果未能解决你的问题,请参考以下文章

在调度一个动作和 NgRx 效果之前执行多个 API 调用

ngrx 8影响其他类型的调度动作

ngRx 状态更新和效果执行顺序

如何在ngrx/effect(redux-observable)中调度多个动作?

ngrx 测试效果动作失败

ngrx effect 多次调用observer,但只调度了一个action