Tha*_*ynh 34 javascript promise observable typescript angular
我正在使用angular 2常见的http来返回一个Observable,但是当我使用嵌套的Observable调用时,我遇到的问题是我的代码喜欢网格:
this.serviceA.get().subscribe((res1: any) => {
this.serviceB.get(res1).subscribe((res2: any) => {
this.serviceC.get(res2).subscribe((res3: any) => {
})
})
})
Run Code Online (Sandbox Code Playgroud)
现在我想使用async/await来避免这种情况,但async/await只适用于Promise.我知道Observable可以转换为Promise,但据我所知,这不是一个好习惯.那我该怎么办?
顺便说一句,如果有人能给我一个示例代码来解决这个问题,那将是很好的async/await:D
Pac*_*ac0 48
关于您的代码示例,如果您想链接Observables(在上一次发出之后触发另一个),请使用flatMap(或switchMap)用于此目的:
this.serviceA.get()
.flatMap((res1: any) => this.serviceB.get())
.flatMap((res2: any) => this.serviceC.get())
.subscribe( (res3: any) => {
....
});
Run Code Online (Sandbox Code Playgroud)
与嵌套相比,这是一个更好的练习,因为这将使事情更清晰,并帮助你避免回调地狱,Observable和Promises应该首先帮助预防.
另外,考虑使用switchMap代替flatMap,基本上它将允许"取消"其他请求,如果第一个请求发出新值.如果触发其余部分的第一个Observable是按钮上的某个点击事件,则可以使用.
如果您不需要各种请求依次等待,您可以使用forkJoin或zip立即启动它们,有关详细信息和其他见解,请参阅@Dan Macak答案.
关于Observables和Angular,你可以| async在Angular模板中完美地使用管道,而不是在组件代码中订阅Observable,以获得此Observable发出的值.
如果你没有直接使用Observable,你可以简单地使用.toPromise()你的Observable,然后使用一些async/await指令.
如果您的Observable只返回一个结果(就像基本API调用的情况一样),Observable可以看作与Promise完全等效.
但是,我不确定是否有任何需要这样做,考虑到Observable已经提供的所有东西(对读者来说:欢迎具有启发性的反例!).作为一项训练练习,我会更乐意随时使用Observables.
一些有趣的博客文章(还有很多其他的):
https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875
toPromise函数实际上有点棘手,因为它不是真正的"运算符",而是一种特定于RxJS的订阅Observable并将其包装在promise中的方法.一旦Observable完成,promise将解析为Observable的最后一个值.这意味着如果Observable发出值"hi"然后在它完成之前等待10秒,则返回的promise将在解析"hi"之前等待10秒.如果Observable永远不会完成,那么Promise永远不会解决.
注意:使用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
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您可以同时启动所有请求,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.
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*cák 13
由于@ Pac0已经详细阐述了各种解决方案,我将只添加稍微不同的角度.
我个人不喜欢混合使用Promises和Observables - 这是你在使用异步等待与Observables时得到的,因为即使它们看起来相似,它们也是非常不同的.
现在即使有时使用两者都是有效的,特别是对于Angular我认为应该考虑尽可能地使用RxJS.原因是:
async管道,允许组合流的整个应用程序数据流,您可以对其进行过滤,组合以及对其进行任何修改,而不会中断来自服务器的数据流,而无需单独或订阅.这样,您不需要打开数据或将其分配给某些辅助变量,数据只是从服务通过Observables直接流向模板,这很漂亮.在某些情况下,Promise仍然可以发光.例如,我在rxjs TypeScript类型中缺少的是单个概念.如果您正在创建一个供他人使用的API,那么返回Observable并不是那么说:您会收到1个值,很多,还是只会完成?你必须写评论来解释它.另一方面,Promise在这种情况下有更清晰的合同.它将始终以1值解析或拒绝错误(除非它当然永远挂起).
通常,您的项目中绝对不需要只有Promises或只有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]) => ...)
Run Code Online (Sandbox Code Playgroud)
这段代码将触发3个请求,一旦所有请求Observable都已完成,订阅回调forkJoin将获得数组中的结果,如上所述,您可以手动订阅它(如示例中所示)或以声明方式执行此操作在模板中使用result$和async管道.
使用Observable.zip将在这里得到你同样的结果,差异之间forkJoin,并zip为前者发出内部观察量只有最后的值,后者结合了内观测量的第一个值,那么第二个值等.
编辑:由于您需要先前HTTP请求的结果,请flatMap在@ Pac0的答案中使用方法.
Chr*_*ton 11
由于toPromise现已在 2022 年弃用。我想展示另一种await在可观察量上使用的方法。我发现这种方法可以使代码更具可读性,而不是又长又复杂的 rxjs 管道。这对于 http 请求特别有用,因为只有一个响应,并且您通常希望在执行其他操作之前等待响应。
更新
我最初的解决方案有效,但 rxjs 具有基本相同的功能:firstValueFrom()。
来自文档:
async function execute() {
const source$ = interval(2000);
const firstNumber = await firstValueFrom(source$);
console.log(`The first number is ${firstNumber}`);
}
Run Code Online (Sandbox Code Playgroud)
原创解决方案
如果您有一个可观察的对象,您可以将其包装在一个承诺中,进行订阅,并在订阅发出时解析。
getSomething(): Promise<any> {
return new Promise((resolve, reject) => {
this.http
.get('www.myApi.com')
.subscribe({
next: (data) => resolve(data),
error: (err) => reject(err),
});
});
}
Run Code Online (Sandbox Code Playgroud)
async现在我们可以在函数内等待响应
async ngOnInit() {
const data = await this.getSomething();
//Do something with your data
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以对数据执行大量复杂的操作,对于不是 rxjs 向导的人来说,它将更容易阅读。如果您有三个相互依赖的后续 http 请求,它将如下所示:
async ngOnInit() {
const first = await this.getFirst();
const second = await this.getSecond(first);
const third = await this.getThird(second);
}
Run Code Online (Sandbox Code Playgroud)
Observables 非常适合流,例如:BehaviorSubject。但是对数据的单次调用(例如http.get())您可能最好使服务调用本身异步。
async getSomethingById(id: number): Promise<Something> {
return await this.http.get<Something>(`api/things/${id}`).toPromise();
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以像这样简单地调用它:
async someFunc(): Promise {
console.log(await getSomethingById(1));
}
Run Code Online (Sandbox Code Playgroud)
RxJS 非常强大,但是将其用于简单的 api 调用似乎太过分了。即使您需要处理检索到的数据,您仍然可以使用 getSomethingById 函数内的 RxJS 运算符并仅返回最终结果。
async/await 的明显优点是阅读起来更清晰,并且您不需要跳过链式调用。
| 归档时间: |
|
| 查看次数: |
19257 次 |
| 最近记录: |