将承诺转化为可观察的


214

我试图把头围在可观察物上。我喜欢可观测技术解决开发和可读性问题的方式。如我所读,好处是巨大的。

HTTP和集合上的可观察对象似乎很简单。我怎样才能将这样的东西转换为可观察的模式。

这是从我的服务组件提供身份验证的。我希望它可以像Angular2中的其他HTTP服务一样工作-支持数据,错误和完成处理程序。

firebase.auth().createUserWithEmailAndPassword(email, password)
  .then(function(firebaseUser) {
    // do something to update your UI component
    // pass user object to UI component
  })
  .catch(function(error) {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

在这里的任何帮助将不胜感激。我唯一的替代解决方案是创建EventEmitter。但是我想这是在服务部分做事的糟糕方法

Answers:


318

如果您使用的是RxJS 6.0.0:

import { from } from 'rxjs';
const observable = from(promise);

9
使用6.3.3,from方法返回observable,但是它将promise作为值发送给订阅。:(
Laxmikant Dange

1
对于RXJS 6+,此答案是正确的。我试图operators通过“直觉” 从导入-我错了。
VSO

119

试试这个:

import 'rxjs/add/observable/fromPromise';
import { Observable } from "rxjs/Observable";

const subscription = Observable.fromPromise(
    firebase.auth().createUserWithEmailAndPassword(email, password)
);
subscription.subscribe(firebaseUser => /* Do anything with data received */,
                       error => /* Handle error here */);

您可以在此处找到对fromPromise运算符的完整参考。


47
import 'rxjs/add/observable/fromPromise';
西蒙·布里格斯

16
import { Observable } from "rxjs/Observable"; :)
Luckylooke

41

1直接执行/转换

用于from将先前创建的承诺直接转换为可观察的。

import { from } from 'rxjs';

// getPromise() will only be called once
const observable$ = from(getPromise());

observable$将是一个热点观察,可以有效地重现对订户的承诺值。

创建observable时,promise主体正在执行或已经解析。如果内部承诺已解决,则可观察对象的新订户将立即获得其价值。

2每个订阅上的递延执行

使用defer与承诺工厂函数作为输入推迟的承诺可观察到的创建和转换。

import { defer } from 'rxjs';

// getPromise() will be called every time someone subscribes to the observable$
const observable$ = defer(() => getPromise());

observable$感冒的

与之不同的from是,defer等待订户,然后才通过调用给定的Promise工厂函数来创建新的Promise。当您想创建一个可观察但不希望立即执行内部承诺时,这很有用。内部承诺只会在有人订阅可观察对象时执行。每个订户还将获得自己的新可观察值。

3许多运营商直接接受承诺

大多数运营商RxJS相结合(例如mergeconcatforkJoincombineLatest...),或变换观测值(例如switchMapmergeMapconcatMapcatchError...)直接受理的承诺。如果您仍在使用其中之一,则不from必先包装诺言(但是要创建冷的可观察性,您可能仍必须使用defer)。

// Execute two promises simultaneously
forkJoin(getPromise(1), getPromise(2)).pipe(
  switchMap(([v1, v2]) => v1.getPromise(v2)) // map to nested Promise
)

查看文档实现,以查看您使用的运算符是否接受ObservableInputSubscribableOrPromise

type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
// Note the PromiseLike ----------------------------------------------------v
type SubscribableOrPromise<T> = Subscribable<T> | Subscribable<never> | PromiseLike<T> | InteropObservable<T>;

fromdefer示例之间的区别: https : //stackblitz.com/edit/rxjs-6rb7vf

const getPromise = val => new Promise(resolve => {
  console.log('Promise created for', val);
  setTimeout(() => resolve(`Promise Resolved: ${val}`), 5000);
});

// the execution of getPromise('FROM') starts here, when you create the promise inside from
const fromPromise$ = from(getPromise('FROM'));
const deferPromise$ = defer(() => getPromise('DEFER'));

fromPromise$.subscribe(console.log);
// the execution of getPromise('DEFER') starts here, when you subscribe to deferPromise$
deferPromise$.subscribe(console.log);

4
我认为差异就是资本,感谢您指出这一点。
红蜘蛛

1

您还可以使用Subject并从promise 触发其next()函数。请参阅以下示例:

添加如下代码(我使用过服务)

class UserService {
  private createUserSubject: Subject < any > ;

  createUserWithEmailAndPassword() {
    if (this.createUserSubject) {
      return this.createUserSubject;
    } else {
      this.createUserSubject = new Subject < any > ();
      firebase.auth().createUserWithEmailAndPassword(email,
          password)
        .then(function(firebaseUser) {
          // do something to update your UI component
          // pass user object to UI component
          this.createUserSubject.next(firebaseUser);
        })
        .catch(function(error) {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          this.createUserSubject.error(error);
          // ...
        });
    }

  }
}

从组件创建用户,如下所示

class UserComponent {
  constructor(private userService: UserService) {
    this.userService.createUserWithEmailAndPassword().subscribe(user => console.log(user), error => console.log(error);
    }
  }


主题是低级机器。请勿使用主题,除非您要进行扩展rxjs
polkovnikov.ph

我只是提供解决方案。
Shivang Gupta

您至少可以显示new Observable(observer => { ... observer.next() ... })实现它的方法。即使这是对现有知名功能的重新实现,它也将直接回答问题,并且不会对读者造成危害。
polkovnikov.ph '18


0

您可以在promise功能周围添加包装器,以将Observable返回给观察者。

  • 使用defer()运算符创建一个懒惰的 Observable ,该操作符允许您仅在Observer订阅时才创建Observable。
import { of, Observable, defer } from 'rxjs'; 
import { map } from 'rxjs/operators';


function getTodos$(): Observable<any> {
  return defer(()=>{
    return fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(response => response.json())
      .then(json => {
        return json;
      })
  });
}

getTodos$().
 subscribe(
   (next)=>{
     console.log('Data is:', next);
   }
)

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.