RxJS序列等效于promise.then()吗?


83

我过去常常以有前途的方式进行开发,现在我转向RxJS。RxJS的文档没有提供关于如何从承诺链转到观察者序列的非常清晰的示例。

例如,我通常会写多个步骤的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样式重写此Promise链?

Answers:


80

对于数据流(相当于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

一些promise运算符可以直接翻译。例如RSVP.all,或jQuery.when可以替换为Rx.Observable.forkJoin

请记住,您有一堆运算符,这些运算符允许异步转换数据,并执行您无法或将很难执行的任务。Rxjs通过数据的异步序列(序列,即多个异步值)揭示了其所有功能。

对于错误管理,主题要复杂一些。

  • 还有渔获最后还有经营者
  • retryWhen 如果出现错误也可以帮助重复序列
  • 您还可以使用该onError功能处理订户本身中的错误。

要获得精确的语义,请更深入地阅读可在网上找到的文档和示例,或在此处提出特定问题。

这绝对是深入利用Rxjs进行错误管理的一个很好的起点:https ://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html


我总是看到可观察到的序列以subscribe()结尾。由于这只是可观察对象的功能,请问有什么理由吗?它是启动序列的功能吗?
于浩良2015年

正是如此。如果没有观察者通过订阅传递,那么您的可观察对象将不会发出任何数据,因此您将看不到任何数据流。
user3743222 2015年

7
我确实建议您看一下:gist.github.com/staltz/868e7e9bc2a7b8c1f754。IT可能比官方文档更可口。
user3743222

3
Promise.then是而.flatMap不是.map
塔玛斯·赫格杜斯

1
仅供参考,这并不完全等效,因为Promise版本3中的错误then会被捕获catch。这里不是。
mik01aj

35

一个更现代的选择:

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

还要注意,要使所有这些工作正常进行,您需要subscribe将此内容通过管道传递到Observable某个地方,但是我认为它是在应用程序的其他部分中处理的。


我是RxJS的新手,但是鉴于我们在这里只处理一个事件的初始流,mergeMap()因此实际上没有任何要合并的内容,我相信在这种情况下,使用concatMap()switchMap()。我知道这个正确的...吗?
丹·金

8

使用RxJs 6更新于2019年5月

同意上面提供的答案,希望使用RxJs v6添加一些玩具数据和简单的Promise(带有setTimeout)来增加清晰度。

只需将传递的ID(当前被硬编码为1)更新为不存在的错误,即可执行错误处理逻辑。重要的是,还要注意ofwithcatchError消息的使用。

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);

输出数据:

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 }

关键部分,等同于以下使用简单承诺控制流的内容:

getPostById(1)
  .then(post => {
    return hydrateAuthor(post);
  })
  .then(post => {
    return dehydratePostTitle(post);
  })
  .then(author => {
    console.log(author);
  })
  .catch(err => {
    console.error(err);
  });

0

如果getPromise函数位于流管道的中间,则应将其简单地包装到函数之一中mergeMapswitchMap或者concatMap(通常是mergeMap):

stream$.pipe(
   mergeMap(data => getPromise(data)),
   filter(...),
   map(...)
 ).subscribe(...);

如果要使用来启动流,getPromise()则将其包装为from函数:

import {from} from 'rxjs';

from(getPromise()).pipe(
   filter(...)
   map(...)
).subscribe(...);


0

如果我理解正确,则意味着使用这些值,在这种情况下,请使用sbuscribe即

const arrObservable = from([1,2,3,4,5,6,7,8]);
arrObservable.subscribe(number => console.log(num) );

另外,您可以使用toPromise()将可观察对象变为promise,如下所示:

arrObservable.toPromise().then()

0

这就是我的方法。

先前

  public fetchContacts(onCompleteFn: (response: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => void) {
    const request = gapi.client.people.people.connections.list({
      resourceName: 'people/me',
      pageSize: 100,
      personFields: 'phoneNumbers,organizations,emailAddresses,names'
    }).then(response => {
      onCompleteFn(response as gapi.client.Response<gapi.client.people.ListConnectionsResponse>);
    });
  }

// caller:

  this.gapi.fetchContacts((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
      // handle rsp;
  });

之后(ly?)

public fetchContacts(): Observable<gapi.client.Response<gapi.client.people.ListConnectionsResponse>> {
    return from(
      new Promise((resolve, reject) => {
        gapi.client.people.people.connections.list({
          resourceName: 'people/me',
          pageSize: 100,
          personFields: 'phoneNumbers,organizations,emailAddresses,names'
        }).then(result => {
          resolve(result);
        });
      })
    ).pipe(map((result: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
      return result; //map is not really required if you not changing anything in the response. you can just return the from() and caller would subscribe to it.
    }));
  }

// caller

this.gapi.fetchContacts().subscribe(((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
  // handle rsp
}), (error) => {
  // handle error
});

副作用:更改检测在将回调转换为可观察值之后也开始起作用 。
阿南·洛克兹(Annand Rockzz),

0

RxJS序列等效于promise.then()吗?

例如

function getdata1 (argument) {
        return this.http.get(url)
            .map((res: Response) => res.json());
    }

    function getdata2 (argument) {
        return this.http.get(url)
            .map((res: Response) => res.json());
    }

    getdata1.subscribe((data1: any) => {
        console.log("got data one. get data 2 now");
        getdata2.subscribe((data2: any) => {
            console.log("got data one and two here");
        });
    });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.