簡體   English   中英

RxJS 序列等同於 promise.then()?

[英]RxJS sequence equivalent to promise.then()?

我曾經有很多承諾,現在我正在轉向 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 風格重寫這個 promise 鏈?

對於數據流(相當於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 轉換為可觀察對象。

一些 promise 運算符有直接翻譯。 例如RSVP.alljQuery.when可以替換為Rx.Observable.forkJoin

請記住,您有一堆操作符可以異步轉換數據,並執行您不能或很難用 promises 完成的任務。 Rxjs 通過異步數據序列(即多於 1 個異步值的序列)揭示了它的所有功能。

對於錯誤管理,這個主題有點復雜。

  • catchfinally運營商
  • 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
   })
)

另請注意,要使所有這些工作正常,您需要在某處subscribe此管道Observable ,但我假設它是在應用程序的其他部分處理的。

2019 年 5 月更新,使用 RxJs 6

同意上面提供的答案,希望使用RxJs v6添加一個帶有一些玩具數據和簡單承諾(使用 setTimeout)的具體示例以增加清晰度。

只需將傳遞的 id(當前硬編碼為1 )更新為不存在的內容即可執行錯誤處理邏輯。 重要的of ,還要注意使用 with with catchError消息。

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

如果我理解正確,你的意思是消耗這些值,在這種情況下你使用 sbuscribe 即

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

此外,您可以使用 toPromise() 將可觀察對象轉換為承諾,如下所示:

arrObservable.toPromise().then()

如果getPromise函數位於流管道的中間,您應該將其簡單地包裝到函數mergeMapswitchMapconcatMap (通常是mergeMap )之一中:

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

如果你想用getPromise()開始你的流然后將它包裝到from函數中:

import {from} from 'rxjs';

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

據我剛剛發現,如果您在 flatMap 中返回結果,它會將其轉換為數組,即使您返回的是字符串。

但是如果你返回一個 Observable,那個 observable 可以返回一個字符串;

我就是這樣做的。

之前

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

之后(是?)

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM