只运行一次订阅
Posted
技术标签:
【中文标题】只运行一次订阅【英文标题】:Run subscription only once 【发布时间】:2018-05-22 23:14:55 【问题描述】:我只需要运行一次userProfiles$.subscribe(async res =>
。但它可以无限工作。你能告诉我如何避免它吗?
这是video of that issue:
.ts
async loginWithGoogle(): Promise<void>
try
const result = await this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
const userId: string = result.additionalUserInfo.profile.id;
const userProfile: AngularFirestoreDocument<UserProfile> = this.fireStore.doc(`userProfile/$userId`);
const userProfiles: AngularFirestoreCollection<UserProfile> = this.fireStore.collection('userProfile/', ref => ref.where('email', '==', result.additionalUserInfo.profile.email));
const userProfiles$: Observable<UserProfile[]> = userProfiles.valueChanges();
userProfiles$.subscribe(async res => //problem is here
if (res.length == 0)
await userProfile.set(
id: userId,
email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
else
await userProfile.update(
lastSignInTime: moment().format()
);
);
catch (err)
console.log(err);
我已尝试将其转换为promise
,如下所示。但没有区别。也许我做错了?
userProfiles$.map(async res =>
if (res.length == 0)
await userProfile.set(
id: userId, email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
).toPromise();
版本:
"typescript": "2.4.2"
"rxjs": "5.5.2",
【问题讨论】:
您是否考虑过将Observable
转换为只执行一次的Promise
?
你也可以.take(1)
收到一个值后强制订阅完成。
我试过了。但没有区别。请查看更新@Szarik
@Sampath 也许在observer
上调用.complete()
可以工作吗?
usubscribe()
取数据后的对象
【参考方案1】:
承诺方式:
userProfiles$.toPromise().then((res) =>
if (res.length == 0)
await userProfile.set(
id: userId, email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
).catch(err =>
// handle error
);
首先你把它转换成promise,然后通过.then()
方法监听它并等待resolved的promise。
可观察的.take(1)
userProfiles$.take(1).subscribe(async res => //problem is here
if (res.length == 0)
await userProfile.set(
id: userId,
email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
else
await userProfile.update(
lastSignInTime: moment().format()
);
);
注意toPromise
方法不要忘记从 rxjs 运算符导入 toPromise
,对于 take
,您还应该从 rxjs 导入 take
方法
更新。角度版本 >= 6
由于 Angular 6 需要 rxjs >= 6。现在 take
等运算符现在可以导入并用于 .pipe()
方法。 You can read more here
// somewhere at the top of file
import take from 'rxjs/operators';
userProfiles$.pipe(take(1)).subscribe(async res => //problem is here
if (res.length == 0)
await userProfile.set(
id: userId,
email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
else
await userProfile.update(
lastSignInTime: moment().format()
);
);
【讨论】:
这不起作用[ts] Property 'take' does not exist on type 'Observable<UserProfile[]>'.
。我已经像这样导入了它import map, first, take from 'rxjs/operators';
你的typescript
和rxjs
是什么版本?
只是好奇尝试使用这个(userProfiles$ as any).take(1)...
并检查它是否正常工作。这不是最好的解决方案,但我只是好奇。
Promise 方式根本行不通。也没有错误。只是不要触发该方法。
这里也是(userProfiles$ as any).take(1)
。不要触发那个方法。【参考方案2】:
我在这里找到了一个同时使用Angularfire2
和firebase native API
的解决方案。
我从Michael here得到了这个解决方案。
Firestore native API
get
get() returns firebase.firestore.QuerySnapshot
Executes the query and returns the results as a QuerySnapshot.
Returns
non-null firebase.firestore.QuerySnapshot A promise that will be resolved with the results of the query.
.ts
constructor()
this.db = firebase.firestore();
async loginWithGoogle(): Promise<string>
try
const response = await this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider());
const result = await this.afAuth.auth.getRedirectResult();
this.user = result.user;
const userId: string = result.user.uid;
const userProfile: AngularFirestoreDocument<UserProfile> = this.fireStore.doc(`userProfile/$userId`);
const res = await this.db.collection("userProfiles").where("email", "==", data).get();
if (res.docs.length === 0)
await userProfile.set(
id: userId,
email: result.additionalUserInfo.profile.email,
creationTime: moment().format(),
lastSignInTime: moment().format()
);
else
await userProfile.update(
lastSignInTime: moment().format()
);
return result.additionalUserInfo.profile.email;
catch (err)
console.log(err);
【讨论】:
以上是关于只运行一次订阅的主要内容,如果未能解决你的问题,请参考以下文章
paho-mqtt实现多客户端订阅一个主题,并保证消息只被接收一次
在 Angular 7 中,为啥即使只订阅一次 HttpClient.post 也会执行两次?