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();
}
