Javascript 将 Observable 与 async/await 一起使用是一个好习惯吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49596641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:24:36  来源:igfitidea点击:

Is it a good practice using Observable with async/await?

javascriptangulartypescriptpromiseobservable

提问by Thach Huynh

I am using angular 2 common http that return an Observable, but I face with a problem that my code likes a mesh when I use nested Observable call:

我正在使用返回 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) => {

       })
   })
})

Now I wanna use async/await to avoid that but async/await only work with Promise. I know that Observable can be converted to Promise but as I know, it is not a good practice. So what should I do here ?

现在我想使用 async/await 来避免这种情况,但 async/await 只适用于 Promise。我知道 Observable 可以转换为 Promise 但据我所知,这不是一个好习惯。那么我应该在这里做什么?

BTW, it will be nice if anyone can give me an example code to solve this with async/await :D

顺便说一句,如果有人能给我一个示例代码来用 async/await 解决这个问题,那就太好了:D

回答by Pac0

Chaining Observables in sequence, as you want to do in your code

按顺序链接 Observables,就像你想在你的代码中做的那样

Concerning your code example, if you want to chain Observables (trigger another after the previous emits), use flatMap(or switchMap) for this purpose :

关于您的代码示例,如果您想链接 Observables(在前一个发出后触发另一个),请为此目的使用flatMap(或switchMap):

this.serviceA.get()
  .flatMap((res1: any) => this.serviceB.get())
  .flatMap((res2: any) => this.serviceC.get())
  .subscribe( (res3: any) => { 
    .... 
  });

This one is better practice compared to nesting, as this will make things clearer and help you avoid callback hell, that Observable and Promises were supposed to help preventing in the first place.

与嵌套相比,这是更好的实践,因为这将使事情更清晰并帮助您避免回调地狱,而 Observable 和 Promise 应该首先帮助防止。

Also, consider using switchMapinstead of flatMap, basically it will allow to 'cancel' the other requests if the first one emits a new value. Nice to use if the first Observable that triggers the rest is some click event on a button, for instance.

此外,考虑使用switchMap而不是flatMap,如果第一个请求发出新值,它基本上将允许“取消”其他请求。例如,如果触发其余部分的第一个 Observable 是按钮上的某个点击事件,则使用起来很好。

If you don't need your various requests to wait in turn for each other, you can use forkJoinor zipto start them all at once, see @Dan Macak answer'sfor details and other insights.

如果您不需要各种请求轮流等待彼此,您可以使用forkJoinzip同时启动它们,请参阅@Dan Macak 的回答以了解详细信息和其他见解。



Angular 'async' pipe and Observables work well together

Angular 'async' 管道和 Observable 可以很好地协同工作

Concerning Observables and Angular, you can perfectly use | asyncpipe in a Angular template instead of subscribing to the Observable in your component code, to get the value(s) emitted by this Observable

关于 Observables 和 Angular,您可以完美地| async在 Angular 模板中使用管道,而不是在组件代码中订阅 Observable,以获取此 Observable 发出的值



ES6 async / await and Promises instead of Observables ?

ES6 async / await 和 Promises 而不是 Observables ?

if you're not feeling using Observable directly, you can simply use .toPromise()on your Observable, and then some async/await instructions.

如果你不觉得直接使用 Observable,你可以简单地.toPromise()在你的 Observable 上使用,然后是一些 async/await 指令。

If your Observable is supposed to return only one result (as it is the case with basic API calls) , an Observable can be seen as quite equivalent to a Promise.

如果您的 Observable 应该只返回一个结果(就像基本 API 调用的情况一样),那么 Observable 可以被视为与 Promise 相当。

However, I'm not sure there is any need to do that, considering all the stuff that Observable already provide(to readers : enlightening counter-examples are welcome!) . I would be more in favor of using Observables whenever you can, as a training exercise.

但是,考虑到 Observable 已经提供的所有内容,我不确定是否需要这样做(对读者来说:欢迎提供启发性的反例!)。我更倾向于尽可能使用 Observables 作为训练练习。



Some interesting blog article on that (and there are plenty of others):

一些有趣的博客文章(还有很多其他的):

https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875

https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875

The toPromise function is actually a bit tricky, as it's not really an “operator”, rather it's an RxJS-specific means of subscribing to an Observable and wrap it in a promise. The promise will resolve to the last emitted value of the Observable once the Observable completes. That means that if the Observable emits the value “hi” then waits 10 seconds before it completes, the returned promise will wait 10 seconds before resolving “hi”. If the Observable never completes, then the Promise never resolves.

NOTE: using toPromise() is an antipattern except in cases where you're dealing with an API that expects a Promise, such as async-await

toPromise 函数实际上有点棘手,因为它并不是真正的“运算符”,而是一种 RxJS 特定的订阅 Observable 并将其包装在承诺中的方法。一旦 Observable 完成,promise 将解析为 Observable 最后发出的值。这意味着如果 Observable 发出值“hi”然后在它完成之前等待 10 秒,返回的 Promise 将在解析“hi”之前等待 10 秒。如果 Observable 永远不会完成,那么 Promise 永远不会解决。

注意:使用 toPromise() 是一种反模式,除非您正在处理需要 Promise 的 API,例如 async-await

(emphasis mine)

(强调我的)



The example you requested

你要求的例子

BTW, it will be nice if anyone can give me an example code to solve this with async/await :D

顺便说一句,如果有人能给我一个示例代码来用 async/await 解决这个问题,那就太好了:D

Example if you reallywant to do it (probably with some mistakes, can't check right now, please feel free to correct)

例子如果你真的想这样做(可能有一些错误,现在无法检查,请随时纠正)

// 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
}

In the case you can start all requests simultaneously, await Promise.all()which should be more efficient, because none of the calls depends on the result of each other. (as would forkJoindo with Observables)

在这种情况下,您可以同时启动所有请求,await Promise.all()这应该会更有效率,因为没有一个调用依赖于彼此的结果。(就像forkJoinObservable 一样)

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.
}

回答by Dan Macák

As @Pac0 already elaborated on the various solutions well, I will just add slightly different angle.

由于@Pac0 已经很好地阐述了各种解决方案,我将添加稍微不同的角度。

Mixing Promises and Observables

混合 Promise 和 Observable

I personally prefer not mixing Promises and Observables- which is what you get while using async await with Observables, because even though they look similar, they are very different.

我个人喜欢将 Promises 和 Observables 混合使用——这是你在使用 async await 和 Observables 时得到的,因为即使它们看起来相似,它们也非常不同。

  • Promises are always async, Observables not necessarily
  • Promises represent just 1 value, Observables 0, 1 or many
  • Promises have very limited use, you can't eg. cancel them (put aside ES next proposals), Observables are so much more powerful in their use (you can manage for example multiple WS connections with them, try that with Promises)
  • Their APIs differ greatly
  • Promises 总是异步的,Observables 不一定
  • Promises 只代表 1 个值,Observables 0、1 或许多
  • 承诺的用途非常有限,你不能例如。取消它们(把 ES 的下一个提案放在一边),Observables 在它们的使用中更加强大(你可以管理例如多个 WS 与它们的连接,用 Promises 尝试)
  • 它们的 API 差别很大


Use of Promises in Angular

在 Angular 中使用 Promise

Now even though it is sometimes valid to use both, especially with Angular I think one should consider going as far with RxJS as possible. The reasons being:

现在,即使有时两者都使用是有效的,尤其是在 Angular 中,我认为应该考虑尽可能使用 RxJS。原因如下:

  • Great portion of Angular API uses Observables(router, http ...), so one kind of goes with and not against the stream (no pun intended) by using RxJS, otherwise one would have to convert to Promises all the time while making up for the lost possibilities RxJS provides
  • Angular has powerful asyncpipe which allows for composing your whole application data flow of streams which you filter, combine and do whatever modification you want on it without interrupting the stream of data coming from server without a single need for thening or subscribing. This way, you don't need to unwrap the data or assign it to some auxiliary variables, the data just flows from services through Observables straight to the template, which is just beautiful.
  • Angular API 的很大一部分使用 Observables(路由器,http ...),所以通过使用 RxJS 一种与流相配而不是逆流(没有双关语意),否则人们将不得不在弥补时一直转换为 Promises对于 RxJS 提供的失去的可能性
  • Angular 具有强大的async管道,它允许组合您的整个应用程序流数据流,您可以对其进行过滤、组合和进行任何您想做的修改,而无需中断来自服务器的数据流,而无需进行任何处理或订阅。这样,你不需要解包数据或将其分配给一些辅助变量,数据只是从服务通过 Observables 直接流向模板,这很漂亮。

There are some cases though where Promise still can shine. For example what I am missing in rxjsTypeScript types is concept of single. If you are creating an API to be used by others, returning Observable is not all that telling: Will you receive 1 value, many, or will it just complete? You have to write comment to explain it. On the other hand, Promise has much clearer contract in this case. It will always resolve with 1 value or reject with error (unless it hangs forever of course).

但在某些情况下,Promise 仍然可以发光。比如我所缺少rxjs打字稿类型是概念。如果您正在创建一个供其他人使用的 API,返回 Observable 并不能说明一切:您会收到 1 个值,多个值,还是只是完成?你必须写评论来解释它。另一方面,Promise 在这种情况下有更清晰的契约。它将始终以 1 个值解析或因错误而拒绝(当然,除非它永远挂起)。

Generally, you definitely don't need to have only Promises or only Observables in your project. If you just want to express with a value that something was completed(deleting user, updating user), and you want to react on it without integrating it to some stream, Promise is the more natural way of doing so. Also, using async/awaitgives you the power to write code in sequential manner and therefore simplifying it greatly, so unless you need advanced management of incoming values, you can stay with Promise.

通常,您的项目中绝对不需要只有 Promise 或只有 Observables。如果您只想用一个值表示某事已完成(删除用户、更新用户),并且您想对其做出反应而不将其集成到某个流中,那么 Promise 是更自然的方式。此外,使用async/await使您能够以顺序方式编写代码并因此大大简化它,因此除非您需要对传入值进行高级管理,否则您可以继续使用 Promise。



Back to your example

回到你的例子

So my recomendation is to embrace both the power of RxJS and Angular. Coming back to your example, you can write the code as following (credits for the idea to @Vayrex):

所以我的建议是同时拥抱 RxJS 和 Angular的强大功能。回到您的示例,您可以编写如下代码(感谢@Vayrex 的想法):

this.result$ = Observable.forkJoin(
  this.serviceA.get(),
  this.serviceB.get(),
  this.serviceC.get()
);

this.result$.subscribe(([resA, resB, resC]) => ...)

This piece of code will fire 3 requests and once all of those request Observables have completed, subscription callback to forkJoinwill get you the results in an array, and as said, you can subscribe to it manually (as in the example) or do this declaratively using result$and asyncpipe in the template.

这段代码将触发 3 个请求,一旦所有这些请求 Observables 都完成,订阅回调forkJoin将为您提供数组中的结果,如上所述,您可以手动订阅它(如示例中所示)或以声明方式执行此操作在模板中使用result$async管道。

Using Observable.zipwould get you the same result here, the difference between forkJoinand zipis that the former emits only last values of inner Observables, the latter combines first values of the inner Observables, then second values etc.

使用Observable.zip将在这里得到你同样的结果之间的差异forkJoin,并zip在于,前者仅发出内部观测量的最后的值,后者结合了内观测量的第一个值,那么第二个值等。



Edit:Since you need the results of previous HTTP requests, use flatMapapproach in @Pac0's answer.

编辑:由于您需要先前 HTTP 请求的结果,请flatMap在@Pac0 的回答中使用方法。