Converter Promise em Observable

214

Estou tentando envolver minha cabeça em observáveis. Adoro a maneira como os observáveis ​​resolvem problemas de desenvolvimento e legibilidade. Enquanto leio, os benefícios são imensos.

Observáveis ​​em HTTP e coleções parecem ser diretos. Como posso converter algo assim em padrão observável.

Isso é do meu componente de serviço, para fornecer autenticação. Eu prefiro que isso funcione como outros serviços HTTP no Angular2 - com suporte para manipuladores de dados, erros e conclusão.

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;
    // ...
  });

Qualquer ajuda aqui seria muito apreciada. A única solução alternativa que tive foi criar EventEmitters. Mas acho que é uma maneira terrível de fazer as coisas na seção de serviços

Krishnan Sriram
fonte

Respostas:

315

Se você estiver usando o RxJS 6.0.0:

import { from } from 'rxjs';
const observable = from(promise);
Guillaume
fonte
9
Usando 6.3.3, frommétodo retornando observável, mas está enviando promessa como valor para assinaturas. :(
Laxmikant Dange
1
Esta resposta está correta para RXJS 6+. Tentei importar da operators"intuição" - estava errado.
VSO
119

tente isto:

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

Você pode encontrar uma referência completa ao operador fromPromise aqui .

Padrinho
fonte
47
import 'rxjs/add/observable/fromPromise';
Simon Briggs
16
import { Observable } from "rxjs/Observable"; :)
Luckylooke
40

1 Execução / Conversão Direta

Use frompara converter diretamente uma promessa criada anteriormente em um observável.

import { from } from 'rxjs';

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

observable$será um observável quente que efetivamente repete o valor prometido aos assinantes.

O organismo de promessas está sendo executado ou já foi resolvido quando o observável é criado. Se a promessa interna tiver sido resolvida, um novo assinante do observável receberá seu valor imediatamente.

2 Execução Adiada em Cada Assinatura

Use defercom uma função de fábrica de promessas como entrada para adiar a criação e conversão de uma promessa em um observável.

import { defer } from 'rxjs';

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

observable$será um observável frio .

A diferença fromé que deferespera por um assinante e só então cria uma nova promessa chamando a função de fábrica de promessa fornecida. Isso é útil quando você deseja criar um observável, mas não deseja que a promessa interna seja executada imediatamente. A promessa interior só será executada quando alguém se inscrever no observável. Cada assinante também terá seu próprio novo observável.

3 Muitos operadores aceitam promessas diretamente

A maioria dos operadores RxJS que combinam (por exemplo merge, concat, forkJoin, combineLatest...) ou transformar observáveis (por exemplo switchMap, mergeMap, concatMap, catchError...) aceitar promessas diretamente. Se você estiver usando um deles de qualquer maneira, não precisará fromprimeiro cumprir uma promessa (mas para criar um observável frio, você ainda precisará usar defer).

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

Verifique a documentação ou implementação para ver se o operador que você está usando aceita ObservableInputou SubscribableOrPromise.

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

A diferença entre frome deferem um exemplo: 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);
fridoo
fonte
4
Eu acho que essa diferença é capital, obrigado por apontar.
Starscream
1

Você também pode usar um Assunto e acionar sua próxima função () da promessa. Veja a amostra abaixo:

Adicione código como abaixo (usei serviço)

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);
          // ...
        });
    }

  }
}

Criar usuário do componente como abaixo

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

Shivang Gupta
fonte
Os assuntos são máquinas de baixo nível. Não use assuntos, exceto nos casos em que estiver estendendo rxjs.
polkovnikov.ph
Estou apenas dando uma solução.
Shivang Gupta
Você poderia pelo menos ter mostrado uma new Observable(observer => { ... observer.next() ... })maneira de implementá-lo. Mesmo que fosse uma reimplementação da função conhecida existente, responderia diretamente à pergunta e não seria prejudicial aos leitores.
Pol4provoc #
1

Você também pode usar adiar . A principal diferença é que a promessa não vai resolver ou rejeitar avidamente.

Mateja Petrovic
fonte
0

Você pode adicionar um wrapper à funcionalidade de promessa para retornar um Observable ao observador.

  • Criando um Observable Lazy usando o operador defer () que permite criar o Observable somente quando o Observer se inscrever.
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);
   }
)

Khizer
fonte