网站搜索

angular - Router 在 async/await 方法中立即执行导航


我有一个 组件 A,它有一个 异步方法 abc()。在这个方法中,我正在等待另一个 异步方法 xyz(),它在 服务 X 内。在这个 xyz() 方法中,我还有其他 await 调用(我需要按顺序执行调用,并在下一个请求中使用响应)。可能会发生这样的情况:在两个特定调用结果为负的情况下,我希望将用户重定向到两个页面中的一个(目前假设只有一个页面)。我正在使用 router.navigate(['/error']),但它并没有像我预期的那样工作……我期望它会停止代码的进一步执行并立即进行重定向。

x.service.ts

async ngOnInit() {
   await this.abc();
}

async abc(): Promise<any> {
   await this.serviceX.xyz();

   // below calls are still executed even if we entered the navigation line in xyz() method
   await this.service3.magicMethod();
   await this.service4.anotherMethod();
}

a.component.ts

async xyz(): Promise<any> {
   const result = await this.service1.getData(); // 
   if (result !== 'OK') {
      this.router.navigate(['/error']);
   }

   const userAnswer = await this.service2.userSelection();
   if (userAnswer !== 'OK') {
      this.router.navigate(['/error']);
   }
}

service1 和 service2 都有异步函数,我在其中使用 toPromise() 等待 HTTP 响应。大概是这样的:

async getData(): Promise<SomeResponse> { // or userSelection()
   const response = await this.wrapperService.getHttpResponse().toPromise();
   console.log(response);
   return response as SomeResponse;
}

答案#1

如果你期望处理停止,那么你需要告诉它停止。除非另有指示,否则在调用的函数 router.navigate 中的处理将会继续进行。

在你的情况下,看起来你在服务中执行路由,这会将 UI 相关的关注点泄漏到服务层。我会重构你的服务方法,使其返回一个表示成功或失败的结果。然后你的组件可以选择导航或继续处理。

service.ts

async xyz(): Promise<boolean> {
   const result = await this.service1.getData(); // 
   if (result !== 'OK') {
       // no further processing
      return false;
   }

   const userAnswer = await this.service2.userSelection();
   if (userAnswer !== 'OK') {
      // no further processing
      return false;
   }

   return true;
}

组件.ts

async ngOnInit() {
   await this.abc();
}

async abc(): Promise<any> {
   const success = await this.serviceX.xyz();
   if (!success) {
     this.router.navigate(['/error']);
     // no further processing
     return;
   }

   await this.service3.magicMethod();
   await this.service4.anotherMethod();
}

我在你调用 router.navigate 之后添加了 return; 语句,以阻止进一步处理。

如果你需要从你的服务返回比 true/false 更多的信息,那么你可以返回一个错误信息或某种对象。不过,一个布尔值足以解释这个概念。

编辑:

显然,如果你真的想在服务中执行路由,没有什么可以阻止你在那里这样做。你仍然会遵循返回成功结果的模式,只是导航现在发生在服务中,在每个return false;语句之前。

编辑 2:

正如我们在评论中讨论的,我并不是想左右你的设计决策,只是建议一个你可以解决问题的模式。

返回错误结果然后将该结果映射到路径会有更多开销,但我个人仍然更倾向于为了清晰而这样做,而不是将路由和数据调用混在一起。我会想到“意大利面代码”这个词……

这是我保持服务调用和路由分离方法的一个示例。这只是一个模式的演示,而不是可用于生产的代码,但希望能给你一些启发。我不介意多敲一些代码以换取更清晰的设计。

service.ts

export enum XyzError = { ErrorA = 1, ErrorB = 2 }

async xyz(): Promise<XyzReason> {
   const result = await this.service1.getData(); // 
   if (result !== 'OK') {
       return XyzReason.ErrorA;
   }

   const userAnswer = await this.service2.userSelection();
   if (userAnswer !== 'OK') {
      return XyzReason.ErrorB;
   }

   return null;
}

组件.ts

async abc(): Promise<any> {
   const result: XyzReason = await this.serviceX.xyz();
   switch (result)  {
     case XyzReason.ErrorA:
       this.router.navigateByUrl('/errora');
       return;
     case XyzReason.ErrorB:
       this.router.navigateByUrl('/errorb');
       return;
   }

   await this.service3.magicMethod();
   await this.service4.anotherMethod();
}