promise.then()と同等のRxJSシーケンス?


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

このPromiseチェーンをRxJSスタイルでどのように書き直す必要がありますか?

回答:


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.alljQuery.when置き換えることができますRx.Observable.forkJoin

データを非同期に変換したり、promiseでは実行できないタスクや実行するのが非常に難しいタスクを実行したりできる演算子がたくさんあることを覚えておいてください。Rxjsは、データの非同期シーケンス(シーケンス、つまり複数の非同期値)ですべての能力を明らかにします。

エラー管理の場合、主題はもう少し複雑です。

  • あるキャッチし、最終的には事業者は、あまりにも
  • retryWhen エラーが発生した場合にシーケンスを繰り返すのにも役立ちます
  • このonError関数を使用して、サブスクライバー自体のエラーに対処することもできます。

正確なセマンティクスについては、Webで見つけることができるドキュメントと例を詳しく調べるか、ここで特定の質問をしてください。

これは間違いなく、Rxjsを使用したエラー管理をより深く理解するための良い出発点になります:https://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html


観察可能なシーケンスがsubscribe()で終わるのを常に見ています。これは観測可能なオブジェクトの機能にすぎないので、これを行う理由はありますか?シーケンスを開始する機能ですか?
Haoliang Yu 2015

まさにそうです。サブスクライブを通過したオブザーバーがない場合、オブザーバブルはデータを出力しないため、データフローは表示されません。
user3743222 2015

7
これをご覧になることをお勧めします:gist.github.com/staltz/868e7e9bc2a7b8c1f754。公式ドキュメントよりも口当たりが良いかもしれません。
user3743222 2015

3
Promise.thenではなく.flatMapです.map
Tamas Hegedus 2016年

1
参考までに、これはPromise、3番目からのバージョンエラーthenがによってキャッチされるため、完全に同等ではありませんcatch。ここではそうではありません。
mik01aj 2018

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に非常に慣れていませんが、ここでは1つのイベントの初期ストリームのみを扱っているmergeMap()ため、実際にマージするものがないことを考えると、この場合は次を使用してまったく同じことを達成できると思います。concatMap()またはswitchMap()。私はこれを正しく理解しましたか...?
ダン・キング

8

RxJs 6を使用して、2019年5月に更新

上記の回答に同意し、RxJs v6を使用して、おもちゃのデータと簡単な約束(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関数は、ストリームパイプの途中にあるおすべき機能の1つに、単純なラップそれを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

私が今知っている限り、flatMapで結果を返すと、文字列を返したとしても、それは配列に変換されます。

ただし、Observableを返す場合、そのobservableは文字列を返すことができます。


0

私が正しく理解していれば、あなたは値を消費することを意味します。その場合、あなたはsbuscribeを使用します。

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

さらに、次のようにtoPromise()を使用して、オブザーバブルをプロミスに変えることができます。

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

副作用:コールバックをobservableに変換した後、変更検出も機能し始めました 。
AnandRockzz19年

0

promise.then()と同等のRxJSシーケンス?

例えば

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");
        });
    });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.