Angular/TS promise 会导致乱序执行吗?

Can Angular/TS promises cause out of order execution?

我们有一个为我们编写的 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 的内部机制了解不多,但我假设传入的事件正在以某种方式排队等待通过路由机制进行传递(我相信后端通过网络套接字将内容推送到前端)。

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

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

现在我们的情况好像和原来描述的一样,空白页,背光灯亮着。

Angular/Typescript甚至可以做到这一点吗?它似乎依赖于这样一个事实,即传入的消息被排队并尽快被处理(以单线程方式),而前端导航(更重要的是,您当前页面的更新记录)可能需要一些时间。

有人可以就此 是否可行 提出建议吗?如果可行,修复它的好方法是什么?

假设 forceNavigateIfNeeded() 等待承诺完成是个坏主意,但我不确定还能怎么做。

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

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

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

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

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

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

抱歉最初的困惑。