【问题标题】:How to wait for a http request to finish in Angular?如何在 Angular 中等待 http 请求完成?
【发布时间】:2018-07-19 16:09:18
【问题描述】:

我一直在使用前端的 Angular 4 和后端的 ASP.NET Core 2 构建一个 SPA。基本上,我试图自动更新用户登录,给定一个有效的令牌。例如,当我重新加载页面时,我希望 Angular 保持登录状态。我将令牌以及用户对象存储在本地存储中,以便我可以检查一些用户属性(如果他/她登录示例)。

我应该如何强制 Angular 等待 http 请求完成? 这是 .net get 服务:

get<T>(url: string): Observable<T> {
    let options = this.doHeaders();
    return this.doSub(this.http.get(this.doUrl(url), options));
}

private doSub<T>(obs: Observable<Response>) {
    var ob1: Observable<T> = obs.map(this.extractData)
        .catch(this.handleError);

    return ob1;
}

这是 HTTP 请求代码:

   getUser() {

    if (this.storage.token == null || this.storage.token == '') {
        return;
    }

    this.net.get<LoginModel>(`Authentication/GetUser`).subscribe(t => {
        this.storage.token = t.token;
        this.storage.user = t.user;
        console.log(this.storage.user);
    });
}

这是我检查用户的方式:

export class StorageService {

    constructor(
        private storage: LocalStorageService
    ) {
    }

    public user: User = null;

    public get isLoggedIn(): boolean {
        return this.user != null;
    }

}

export class IsLoggedIn implements CanActivate {

constructor(
    private storage: StorageService,
    private router: Router
) { }

canActivate(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
) {
    if (this.storage.isLoggedIn) {
        return true;
    }

    this.router.navigate(['account/login'], {
        queryParams: {
            return: state.url,
            message: 'You must be logged in'
        }
    });
    return false;
}}

真正的问题是:每当我尝试在受保护的页面上重新加载页面时,Angular 不会等待 http 完成,而是将我重定向到登录页面。大约几秒钟后,它登录了,但我已经被重定向了。 上面的代码正在 AppComponent (NgOnInit) 上执行。提前致谢。

【问题讨论】:

  • 通常你永远不会等待异步操作在 Angular 中完成。那会阻塞线程。例如,如果您无法获取用户,您可以返回 Observable,然后您重定向(在订阅中)
  • 我不确定我是否理解正确。你的意思是我应该从 AppComponent 中取出订阅并在我检查的任何地方订阅它?
  • 类似的东西。重点是整个应用程序使用可观察对象异步运行。我看到您的 get 用户没有安全性,例如它不要求提供凭据。我想象某种登录页面。你会做的一些事情:如果token为空,去登录,订阅登录结果,如果好继续,否则显示登录错误
  • getUser方法不会同步返回,明白了吗?
  • GetUser 从令牌 (JWT) 中获取用户。如果令牌为空,我可以重定向到登录,但并非我的所有页面都受到保护,所以我只在用户尝试访问受保护的路由时重定向。我知道 Observables 是异步的。我不在乎是否有任何方法可以发出单个同步 http 请求。我想要的只是等待该请求的应用程序。

标签: angular typescript observable


【解决方案1】:

在您的 observable 上使用 .toPromise,然后使用 async/await

await this.net.get<LoginModel>(`Authentication/GetUser`).toPromise();

【讨论】:

  • 感谢您的回复,但是编译器不允许我在订阅之后或之前执行 .toPromise()。可能和我的网和get有关。我已经更新了问题。
  • import 'rxjs/add/operator/toPromise';?
  • 在 .subscribe 之前/之后不起作用。我必须删除订阅才能让编译器接受。 this.net.get&lt;LoginModel&gt;('Authentication/GetUser').toPromise();也许你的意思是:this.net.get&lt;LoginModel&gt;('Authentication/GetUser').toPromise().then(...);
  • 不需要调用subscribe,可以从await ...的返回值中得到t
  • 你不能订阅,你可以在你的方法前添加“await”标志来获取数据。
【解决方案2】:

你可以在 async/await 中使用 Promise

async getUser() {
    if (this.storage.token == null || this.storage.token == '') {
        return;
    }

    const t = await this.net.get<LoginModel>(`Authentication/GetUser`).toPromise();

    this.storage.token = t.token;
    this.storage.user = t.user;

    console.log(this.storage.user);
}

查看更多:

【讨论】:

  • 虽然这编译并获取用户,但它仍然不能解决我的问题。我正在更新更多信息,也许我在其他地方遗漏了一些东西。你能看看吗?
  • 你的调用return this.doSub(this.http.get(this.doUrl(url), options));也应该改成await的get请求,也许你还需要在其他地方重构代码?
  • 我对异步不太了解,抱歉这么业余哈哈。嗯,我会尝试更改 doSub。这是我唯一需要等待的请求,所以也许我会为它创建一个新的请求
  • 请阅读 httpInterceptor angular.io/guide/http#intercepting-requests-and-responses,阅读 switchMap 并忘记 toPromise
  • @RohanAgarwal,在我看来,连接 Promise 有点噩梦。此外,Angular 在 Observables 中进行了思考,“等待”响应是进行同步调用,您等待承诺解决。对我来说,当你得到数据时更自然地拨打电话并控制响应
【解决方案3】:

您不能将 async/await 语法与 Angular HttpClient 返回的 Observable 一起使用,因此您首先需要先将 observable 转换为 Promise。

幸运的是,Observable 接口导出了一个 toPromise() 方法,该方法从 observable 返回一个承诺:

await this.net.get<LoginModel>('Authentication/GetUser').toPromise();

看看这个例子如何use Async/Await with Angular HttpClient

【讨论】:

  • 您添加了重复的答案。
猜你喜欢
  • 2017-07-04
  • 1970-01-01
  • 2018-11-13
  • 1970-01-01
  • 2017-01-21
  • 2017-08-08
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
相关资源
最近更新 更多