我使用的是返回Observable的angular 2 common http,但是当我使用嵌套的Observable调用时,我面临一个问题,即我的代码喜欢网格:
this.serviceA.get().subscribe((res1: any) => {
this.serviceB.get(res1).subscribe((res2: any) => {
this.serviceC.get(res2).subscribe((res3: any) => {
})
})
})
现在我想使用async / await来避免这种情况,但async / await只适用于Promise。我知道Observable可以转换为Promise,但据我所知,这不是一个好习惯。那我该怎么办呢?
顺便说一下,如果有人能给我一个示例代码来解决这个问题,那将很高兴:async / await:D答案 0 :(得分:32)
关于您的代码示例,如果您想链接Observables(在上一次发出后触发另一个),请使用flatMap
(或switchMap
)来实现此目的:
this.serviceA.get()
.flatMap((res1: any) => this.serviceB.get())
.flatMap((res2: any) => this.serviceC.get())
.subscribe( (res3: any) => {
....
});
与嵌套相比,这是一个更好的练习,因为这会使事情更清晰,并帮助你避免回调地狱,Observable和Promises应该首先帮助预防。
另外,考虑使用switchMap
代替flatMap
,基本上它会允许取消'如果第一个发出新值,则另一个请求。如果触发其余部分的第一个Observable是按钮上的某个点击事件,则可以使用。
如果您不需要依次等待各种请求,可以使用forkJoin
或zip
一次启动所有请求,请参阅@Dan Macak answer's细节和其他见解。
关于Observables和Angular,您可以在Angular模板中完美地使用| async
管道,而不是在组件代码中订阅Observable,以获取此Observable发出的值
如果您不想直接使用Observable,可以在Observable上使用.toPromise()
,然后使用async / await指令。
如果您的Observable只返回一个结果(就像基本API调用那样),Observable可以看作与Promise完全等效。
但是,我不确定是否有必要这样做,考虑到Observable已经提供的所有东西(对读者来说:欢迎具有启发性的反例!)。作为一种训练练习,我会更倾向于随时使用Observable。
一些有趣的博客文章(还有很多其他文章):
https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875
toPromise函数实际上有点棘手,因为它不是 真的是一个“操作员”,而不是RxJS特有的手段 订阅一个Observable并将其包装在一个promise中。 承诺 将解析为Observable的最后一次发射值 Observable完成。这意味着如果Observable发出了 值“hi”然后在完成之前等待10秒,返回 承诺将在解决“hi”之前等待10秒。如果是Observable 永远不会完成,然后承诺永远不会解决。
注意:使用toPromise()是一个反模式,除非你是这样的 处理期望Promise的API,例如async-await
(强调我的)
顺便说一下,如果有人能给我一个示例代码来解决它会很好 这与async / await:D
示例如果您真的想要这样做(可能有一些错误,现在无法检查,请随时纠正)
// Warning, probable anti-pattern below
async myFunction() {
const res1 = await this.serviceA.get().toPromise();
const res2 = await this.serviceB.get().toPromise();
const res3 = await this.serviceC.get().toPromise();
// other stuff with results
}
如果您可以同时启动所有请求,await Promise.all()
应该更高效,因为没有一个调用取决于彼此的结果。 (与forkJoin
对Observables一样)
async myFunction() {
const promise1 = this.serviceA.get().toPromise();
const promise2 = this.serviceB.get().toPromise();
const promise3 = this.serviceC.get().toPromise();
let res = await Promise.all([promise1, promise2, promise3]);
// here you can promises results,
// res[0], res[1], res[2] respectively.
}
答案 1 :(得分:10)
由于@ Pac0已经详细阐述了各种解决方案,我只会添加稍微不同的角度。
我个人更喜欢不混合Promises和Observables - 这是你在使用Asservables async await时得到的,因为即使它们看起来很相似,它们也是非常不同的。
现在即使有时使用两者都是有效的,特别是和Angular一起我认为应该考虑尽可能地使用RxJS 。原因是:
async
管道,允许组合流的整个应用程序数据流,您可以对其进行过滤,组合和执行任何修改,而不会中断来自服务器的数据流而无需单身需要订阅或订阅。这样,您不需要打开数据或将其分配给某些辅助变量,数据只是通过Observables直接流向模板,这很漂亮。在某些情况下,承诺仍然可以发光。例如, rxjs TypeScript类型中缺少的是 single 的概念。如果您正在创建一个供他人使用的API,那么返回Observable并不是那么说:您会收到1个值,很多,还是只会完成?你必须写评论来解释它。另一方面,Promise在这种情况下有更清晰的合同。它将始终以1值解析或拒绝并出现错误(除非它当然永远挂起)。
通常,您绝对不需要在项目中只有Promise或只有Observable。如果你只想用一个完成(删除用户,更新用户)的值来表达,并且你想对它做出反应而不将它集成到某个流中,Promise是更自然的做法所以。此外,使用async/await
使您能够以顺序方式编写代码,从而大大简化代码,因此除非您需要对传入值进行高级管理,否则您可以继续使用Promise。
所以我的推荐是拥抱RxJS和Angula r的力量。回到你的例子,你可以编写如下代码(想法归功于@Vayrex):
this.result$ = Observable.forkJoin(
this.serviceA.get(),
this.serviceB.get(),
this.serviceC.get()
);
this.result$.subscribe(([resA, resB, resC]) => ...)
这段代码将触发3个请求,一旦所有请求Observable完成,订阅回调到forkJoin
将获得数组中的结果,如上所述,您可以手动订阅它(如在示例中)或使用模板中的result$
和async
管道以声明方式执行此操作。
使用Observable.zip
会在这里得到相同的结果,forkJoin
和zip
之间的区别在于前者只发出内部Observable的最后一个值,后者组合了第一个值内部的Observables,然后是第二个值等。
修改:由于您需要先前HTTP请求的结果,请在@ Pac0的回答中使用flatMap
方法。
答案 2 :(得分:1)
Observable 非常适合流,例如:BehaviorSubject。但是对于数据的单一调用(例如 http.get()),您最好使服务调用本身异步。
async getSomethingById(id: number): Promise<Something> {
return await this.http.get<Something>(`api/things/${id}`).toPromise();
}
然后,您可以简单地这样称呼它:
async someFunc(): Promise {
console.log(await getSomethingById(1));
}
RxJS 非常强大,但将其用于简单的 api 调用似乎过于矫枉过正。即使您需要处理检索到的数据,您仍然可以使用 getSomethingById 函数中的 RxJS 操作符并返回最终结果。
async/await 的明显优势在于它更清晰易读,而且您不需要跳过箍来进行链式调用。