【问题标题】:Can Angular/TS promises cause out of order execution?Angular/TS 承诺会导致乱序执行吗?
【发布时间】:2021-07-28 10:07:58
【问题描述】:

我们有一个为我们编写的 Angular 应用程序,我们正试图找出一个特定的问题(请参阅下面的我的大量文章)。基本上,这是一个系统,其中一些数字输入应该控制背光和显示的页面。如果数字输入打开,背光也应该打开,应用程序应该正常工作,允许用户交互。

但是,如果没有该数字输入信号,应用程序应该给出一个空白页面并关闭背光,这样用户就什么都看不到并且无法以某种盲目的方式与真实页面进行交互。


背光由后端进程控制,该进程还使用 Angular 路由将信息传递到前端(两者都在同一设备上运行)。但是网页上显示的内容完全在前端的控制之下。

我们看到这样一种情况,如果数字输入被快速关闭和打开,我们有时会打开背光但页面是空白的,我想知道在传递事件时是否存在某种竞争条件通过路由器。


在代码方面,我们有一个效果,它监控来自后端(不是直接)的数字输入信号,看起来像:

@Effect({ dispatch: false })
receiveDigitalInputEvent$ = this.actions.pipe(
  ofType(AppActionTypes.RECEIVE_DIGITAL_INPUT),
  map((action: AppActions.ReceiveDigitalInput) => action.payload),
  withLatestFrom(this.store.pipe(select(getRouterState))),
  tap(([data, routerState]) => {
    const currUrl = routerState ? routerState.state.url : '';
    const { isDigitalInputSet, someOtherStuff } = data;
    this.forceNavigateIfNeeded(isDigitalInputSet, currUrl);
  })
);

forceNavigateIfNeeded(isDigitalInputSet: boolean, currUrl) {
  if (isDigitalInputSet && currUrl.endsWith('/blank')) {
    this.router.navigateByUrl('/welcome');
  else if (! isDigitalInputSet && ! currUrl.endsWith('/blank')) {
    this.router.navigateByUrl('/blank');
  }
}

这基本上是在数字输入事件到达时:

  • 如果输入已打开且屏幕当前为空白,则转到欢迎屏幕;或
  • 如果输入关闭并且您当前不在空白屏幕上,请转到空白屏幕。

现在,这可以正常工作,只是快速转换似乎导致了问题。


我对 Angular 内部结构了解不多,但我认为传入的事件正在以某种方式排队等待通过路由机制进行传递(我相信后端通过 Web 套接字将东西推送到前端)。

我查看了 Angular 源代码并注意到 navigateByUrl 使用承诺来完成其工作,因此代码可能会返回以获取下一个消息队列项在页面实际更改之前。我只是希望有更多 Angular nous 的人来检查我的推理,或者让我知道我的建议是否是垃圾。至于什么,我的建议是:

  1. 数字输入关闭,消息 (OFF) 从后端排队到前端。后端也会关闭背光。
  2. 数字输入再次快速打开,一条消息 (ON) 从后端排队到前端。后端还会重新激活背光。
  3. 前端收到 OFF 消息并进行处理。因为我们在不在空白页时收到了 OFF 消息,所以我们调用 navigateByUrl('/blank') 来启动一个 promise。
  4. 可以实现承诺之前(并且routerState.state.url 从非空白变为空白),我们开始处理 ON 消息。在这个阶段,我们有一个(过时的)非空白页面的 ON 消息,所以 forceNavigateIfNeeded() 什么都不做。
  5. 进行中的navigateByUrl('/blank') 承诺完成,将页面设置为空白。

现在我们似乎遇到了最初描述的情况,一个背光打开的空白页面。

Angular/Typescript 甚至可以实现这一点吗?它似乎依赖于这样一个事实,即进入队列并尽快(以单线程方式)对其采取行动,而前端导航(更重要的是,您当前页面的更新记录)可能需要一段时间。

有人可以建议这是否可能的,如果是,有什么好的方法来解决它?

认为forceNavigateIfNeeded() 等待承诺完成是个坏主意,但我不知道该怎么做。

【问题讨论】:

    标签: javascript angular typescript promise routes


    【解决方案1】:

    虽然您的问题非常复杂,但我可以看到一个值得检查的可疑代码。

    ...
    withLatestFrom(this.store.pipe(select(getRouterState))),
    ...
    

    这可能是您的问题的原因,因为它正在获取那里的最新信息。它不请求更新,也不等待它。我们遇到了类似的问题,毫秒也在起作用,我想出了解决方法,我用switchMap 运算符替换了withLatestFrom。我不确定它是否适用于您的情况,但您可以尝试一下。

    map((action: AppActions.ReceiveDigitalInput) => action.payload),
    switchMap(data => //data === action.payload
     this.store.select(getRouterState).pipe(map(routerState => ({ data, routerState }))
    ),
    tap(({data, routerState}) => { ... }
    

    编辑: 在我们的例子中,我们在switchMap 中使用了一个自定义选择器,它比较当前和以前的状态并返回比较结果。老实说,我不能 100% 确定这是否也会对您有所帮助,但在我们的案例中,这是一个必要的难题。

    export const getFeshRouterState= pipe(
      select(getRouterState),
      startWith(null as RouterStateInterface),
      pairwise(),
      map(([prev, curr]) => /* do some magic here and return result */),
    );
    

    对不起,最初的混乱。

    【讨论】:

    • 所以,要明确一点,您是在暗示 withLatestFrom 可能正在使用路由器状态的某些本地缓存值,而您的修复程序却是为了获得“真实”状态,这是一个准确阅读你的答案?我想这可能也会引起我的问​​题,因为即使navigateByUrl 立即更改routerState.state.url(我仍然不确定),这个事实可能并未传达给所有观察者然而。无论如何,我已将您的注释添加到我们的 JIRA 问题中以供调查,非常感谢您的帮助。
    • 我对最后一个建议有点困惑,尽管我可能误解了。如果问题是getRouterState 被记忆了,我们如何使用一个使用记忆的变体作为来源的非记忆变体来解决这个问题?那不会有同样的问题吗?
    • 很抱歉给您带来了困惑。我编辑了我的答案以更好地解释它。
    猜你喜欢
    • 2012-09-12
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多