我正在创建一个应用程序,用户可以通过他的 Google 帐户来识别自己。在幕后,我使用 gapi 来处理登录。另一方面,有一个称为“用户”的角度服务,Observable
每次用户的状态(在线/离线)发生变化时,它都会向订阅者广播信息。然后,将所有内容粘合在一起,有一个按钮,当用户单击它时,会发生以下情况:
- 一旦 gapi 被初始化,就会创建一个 Promise 来解决。(下面代码的第一行)
- 当 promise 解决时,Google 登录是通过 gapi 执行的。(下面代码的第二行)
- 当登录承诺解决时,向后端发出 AJAX 请求 id 以验证 Google 令牌并返回电子邮件、姓名等信息并订阅此 observable。
this.restService
(下面代码中以开头的行) - 当上面的 observable 发出我的 web 服务返回的值时,我在我的“用户”服务中调用一个函数,该函数在 observable 上发出一个值以表示用户的状态(它广播了用户现在已通过身份验证的事实)。
- 然后,所有订阅者都会收到此信息并知道用户已登录。
这是代码:
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set("data.name", "data.email", "data.picture", AuthenticationType.Google);
});
});
});
问题是代码有时有效,有时无效。经过一番调查,我注意到这与我的 Web 服务执行的持续时间有关。为了确保这一点,我在其中做了一个语句,在 2 秒内暂停请求的执行,在这种情况下,我的代码总是失败。但我所说的“失败”是什么意思?
在页面的某处,我有一个订阅用户服务可观察的组件:
this.userService.getStatus().subscribe(status => {
console.log(status);
this.canPostComment = (status == UserStatus.Online);
});
当 Web 服务执行得非常快时,我会看到console.log
,然后canPostComment
属性会更新,我的视图也会更新,所以没有问题。但是,当 Web 服务需要更长的时间时,我仍然看到console.log
具有正确值但视图没有更新......
怀疑它与 Angular 更改检测有关,我以这种方式使用 zone:
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.zoneService.run(() => {
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set("data.name", "data.email", "data.picture", AuthenticationType.Google);
});
});
});
});
然后它起作用了......所以我读到了 zone 并且我了解到它被用来警告 angular 它必须运行更改检测(或类似的东西......),但我也读到了常见的函数,如setTimeout
或 AJAX 调用已经被“区域”修补了,所以我不明白它是如何解决我的问题的,我也不明白为什么我会遇到这个问题。为什么 Angular 没有看到这种canPostComment
变化?
由于我的代码有点复杂,因此很难破解它,这就是我复制/粘贴大部分相关代码的原因。
编辑
在我提出问题后,我不得不修改一些代码,因为我需要知道整个登录过程何时完成。事实上,当用户点击登录按钮时,它的标题变成了“正在登录...”,一旦整个过程完成,它就会消失。我本可以订阅userService.getStatus
observable,但我决定承诺能够做到:
this.googleAuthenticationService.login.then(() => { ... });
所以我以这种方式更新了上面的代码:
return new Promise((resolve, reject) => {
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.zoneService.run(() => {
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set(data.name, data.email, data.picture, AuthenticationType.Google);
resolve(true)
},
error => reject(false));
});
});
});
});
出于好奇,我试图删除该this.zoneService.run
声明只是为了看看会发生什么,结果证明它没有......所以把所有东西都放在一个承诺中似乎可以解决这个问题......但是,问题仍然存在......为什么第一个例子没有用吗?