我过去经常发展很多,现在我转向RxJS.RxJS的文档没有提供关于如何从promise链到观察者序列的非常明确的例子.
例如,我通常用多个步骤编写promise链,比如
// a function that returns a promise getPromise() .then(function(result) { // do something }) .then(function(result) { // do something }) .then(function(result) { // do something }) .catch(function(err) { // handle error });
我应该如何以RxJS风格重写这个承诺链?
对于数据流(相当于then
):
Rx.Observable.fromPromise(...) .flatMap(function(result) { // do something }) .flatMap(function(result) { // do something }) .subscribe(function onNext(result) { // end of chain }, function onError(error) { // process the error });
承诺可以转换为可观察的Rx.Observable.fromPromise
.
一些承诺运营商有直接翻译.例如RSVP.all
,或者jQuery.when
可以替换为Rx.Observable.forkJoin
.
请记住,您有一堆允许异步转换数据的运算符,并执行您不能或很难用promises执行的任务.Rxjs使用异步数据序列(序列即超过1个异步值)显示其所有功能.
对于错误管理,主题有点复杂.
还有捕获,最后是运营商
retryWhen
如果出错,也可以帮助重复序列
您还可以使用该onError
功能处理订户本身的错误.
要获得精确的语义,请深入了解可在Web上找到的文档和示例,或在此处提出具体问题.
对于使用Rxjs进行更深入的错误管理,这肯定是一个很好的起点:https://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html
一个更现代的选择:
import {from as fromPromise} from 'rxjs'; import {catchError, flatMap} from 'rxjs/operators'; fromPromise(...).pipe( flatMap(result => { // do something }), flatMap(result => { // do something }), flatMap(result => { // do something }), catchError(error => { // handle error }) )
Update May 2019, using RxJs 6
Agree with the provided answers above, wished to add a concrete example with some toy data & simple promises (with setTimeout) using RxJs v6 to add clarity.
Just update the passed id (currently hard-coded as 1
) to something that does not exist to execute the error handling logic too. Importantly, also note the use of of
with catchError
message.
import { from as fromPromise, of } from "rxjs"; import { catchError, flatMap, tap } from "rxjs/operators"; const posts = [ { title: "I love JavaScript", author: "Wes Bos", id: 1 }, { title: "CSS!", author: "Chris Coyier", id: 2 }, { title: "Dev tools tricks", author: "Addy Osmani", id: 3 } ]; const authors = [ { name: "Wes Bos", twitter: "@wesbos", bio: "Canadian Developer" }, { name: "Chris Coyier", twitter: "@chriscoyier", bio: "CSS Tricks and CodePen" }, { name: "Addy Osmani", twitter: "@addyosmani", bio: "Googler" } ]; function getPostById(id) { return new Promise((resolve, reject) => { setTimeout(() => { const post = posts.find(post => post.id === id); if (post) { console.log("ok, post found!"); resolve(post); } else { reject(Error("Post not found!")); } }, 200); }); } function hydrateAuthor(post) { return new Promise((resolve, reject) => { setTimeout(() => { const authorDetails = authors.find(person => person.name === post.author); if (authorDetails) { post.author = authorDetails; console.log("ok, post hydrated with author info"); resolve(post); } else { reject(Error("Author not Found!")); } }, 200); }); } function dehydratePostTitle(post) { return new Promise((resolve, reject) => { setTimeout(() => { delete post.title; console.log("ok, applied transformation to remove title"); resolve(post); }, 200); }); } // ok, here is how it looks regarding this question.. let source$ = fromPromise(getPostById(1)).pipe( flatMap(post => { return hydrateAuthor(post); }), flatMap(post => { return dehydratePostTitle(post); }), catchError(error => of(`Caught error: ${error}`)) ); source$.subscribe(console.log);
Output Data:
ok, post found! ok, post hydrated with author info ok, applied transformation to remove title { author: { name: 'Wes Bos', twitter: '@wesbos', bio: 'Canadian Developer' }, id: 1 }
The key part, is equivalent to the following using plain promise control flow:
getPostById(1) .then(post => { return hydrateAuthor(post); }) .then(post => { return dehydratePostTitle(post); }) .then(author => { console.log(author); }) .catch(err => { console.error(err); });