【问题标题】:Convert Promise to Observable将 Promise 转换为 Observable
【发布时间】:2017-01-12 03:45:31
【问题描述】:

我正试图将我的头脑围绕在可观察对象上。我喜欢 observables 解决开发和可读性问题的方式。正如我所读的,好处是巨大的。

HTTP 和集合上的 Observables 似乎是直截了当的。我怎样才能将这样的东西转换为可观察的模式。

这是来自我的服务组件,提供身份验证。我希望它能够像 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;
    // ...
  });

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

【问题讨论】:

    标签: angular firebase rxjs firebase-authentication


    【解决方案1】:

    如果你使用的是 RxJS 6.0.0:

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

    【讨论】:

    • 使用 6.3.3,from 方法返回 observable 但它正在将承诺作为值发送给订阅。 :(
    • 这个答案对于 RXJS 6+ 是正确的。我试图通过“直觉”从operators 导入 - 我错了。
    【解决方案2】:

    试试这个:

    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 运算符 here 的完整参考。

    【讨论】:

      【解决方案3】:

      1 直接执行/转换

      使用from 直接将之前创建的 Promise 转换为 Observable。

      import { from } from 'rxjs';
      
      // getPromise() is called once, the promise is passed to the Observable
      const observable$ = from(getPromise());
      

      observable$ 将是一个hot Observable,它可以有效地将 Promises 值重播给订阅者。

      它是一个热门的 Observable,因为生产者(在本例中为 Promise)是在 Observable 之外创建的。多个订阅者将共享同一个 Promise。如果内部 Promise 已解决,则 Observable 的新订阅者将立即获得其值。

      2 每次订阅延迟执行

      使用defer 和 Promise 工厂函数作为输入,以推迟 Promise 的创建和转换为 Observable。

      import { defer } from 'rxjs';
      
      // getPromise() is called every time someone subscribes to the observable$
      const observable$ = defer(() => getPromise());
      

      observable$ 将是一个冷 Observable。

      这是一个冷的 Observable,因为生产者(Promise)是在 Observable 内部创建的。每个订阅者都会通过调用给定的 Promise 工厂函数来创建一个新的 Promise。

      这允许您创建observable$,而无需立即创建并执行 Promise,也无需与多个订阅者共享此 Promise。 observable$ 的每个订阅者都有效地调用了from(promiseFactory()).subscribe(subscriber)。因此,每个订阅者都会创建自己的新 Promise 并将其转换为新的 Observable,并将自己附加到这个新的 Observable。

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

      大多数 RxJS 运算符组合(例如 merge、concat、forkJoin、combineLatest ...)或转换可观察对象(例如 switchMap、mergeMap、concatMap、catchError。 ..) 直接接受承诺。如果您仍然使用其中一个,则不必先使用 from 来包装承诺(但要创建 cold observable,您仍然可能必须使用 defer)。

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

      检查documentation 或implementation 以查看您使用的运算符是否接受ObservableInput 或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>;
      

      示例中from和defer的区别: 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);
      

      【讨论】:

      • 我认为差异就是资本,谢谢指出。
      • 感谢分享,很多运营商直接接受promise!直到
      • 非常感谢您提供如此详细的文章!它不仅帮助我解决了我的问题,还帮助我更好地理解了这些概念。
      【解决方案4】:

      将 promise 转换为 observable 的正确 模式 是使用 defer 和 from 运算符:

      import { defer, from } from 'rxjs';
          
      const observable$ = defer(() => from(myPromise()));
      

      为什么我们需要defer 运算符?

      Promises eager,这意味着当被调用时,它们会立即触发。这与可观察对象的工作方式相反。 Observables 是惰性的,它们只有在调用.subscribe() 时才会被触发。这就是我们需要始终将其包装到 defer 运算符中的原因。 from 运算符不做这项工作,所以defer 是总是需要的。

      【讨论】:

        【解决方案5】:

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

        • 使用 defer() 运算符创建一个 Lazy 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);
           }
        )
        
        

        【讨论】:

          【解决方案6】:

          您也可以使用defer。主要区别在于承诺不会急切地解决或拒绝。

          【讨论】:

            【解决方案7】:
            import { from } from 'rxjs';
            
            from(firebase.auth().createUserWithEmailAndPassword(email, password))
            .subscribe((user: any) => {
                  console.log('test');
            });
            

            这是一个较短的版本,它结合了上面的一些答案,将您的代码从 Promise 转换为 observable。

            【讨论】:

            • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
            【解决方案8】:

            您也可以使用 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。
            • 我只是给出一个解决方案。
            • 您至少可以展示new Observable(observer =&gt; { ... observer.next() ... }) 的实现方式。即使它是对现有众所周知的功能的重新实现,它也会直接回答问题并且不会对读者造成伤害。
            【解决方案9】:

            有 Rxjs 提供的 toPromise() 运算符, 就像代码示例演示的那样:

            @Injectable({
              providedIn: 'root'
            })
            export class InventoryService {
              constructor(private httpClient: HttpClient) {}
            
              getCategories(): Observable<Category[]> {
                const url = 'https://www.themealdb.com/api/json/v1/1/categories.php';
            
                return this.httpClient.get<CategoriesResponse>(url).pipe(
                  map(response => response.categories)
                );
              }
            }
            

            在您的组件内部,您可以应用 toPromise() 运算符:

            export class AppComponent {
              categories: any[];
            
              constructor(private inventoryService: InventoryService) {}
            
              public async loadCategories() {
                this.categories = await this.inventoryService
                  .getCategories()
                  .**toPromise()**
            

            但目前 Rxjs7+ 已弃用,建议使用 lastValueFrom() 运算符:

              public async loadCategories() {
                const categories$ = this.inventoryService.getCategories();
                this.categories = await **lastValueFrom**(categories$);
              }
            

            我希望它对更新版本的更新代码有所帮助:')

            【讨论】:

            • 运营商反问
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-25
            • 2018-02-19
            • 2018-08-22
            • 2016-08-15
            • 2021-09-21
            相关资源
            最近更新 更多