Program Tip

promise.then ()과 동일한 RxJS 시퀀스?

programtip 2020. 11. 8. 10:57
반응형

promise.then ()과 동일한 RxJS 시퀀스?


나는 약속을 가지고 많은 것을 개발하곤했고 지금은 RxJS로 이동하고 있습니다. RxJS의 문서는 프라 미스 체인에서 옵저버 시퀀스로 이동하는 방법에 대한 명확한 예를 제공하지 않습니다.

예를 들어, 저는 보통 다음과 같이 여러 단계로 프로 미스 체인을 작성합니다.

// 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 스타일로 어떻게 다시 작성해야합니까?


데이터 흐름의 경우 (에 해당 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.

일부 약속 연산자는 직접 번역합니다. 예를 들어 RSVP.all, 또는 jQuery.when로 대체 될 수 있습니다 Rx.Observable.forkJoin.

데이터를 비동기 적으로 변환하고 약속으로 수행 할 수 없거나 수행하기 매우 어려운 작업을 수행 할 수있는 많은 연산자가 있다는 점을 기억하십시오. Rxjs는 데이터의 비동기 시퀀스 (즉, 하나 이상의 비동기 값)로 모든 능력을 보여줍니다.

오류 관리의 경우 주제는 조금 더 복잡합니다.

  • 거기 캐치마지막으로 통신 사업자도
  • retryWhen 오류가 발생한 경우 시퀀스를 반복하는 데 도움이 될 수도 있습니다.
  • onError함수 를 사용하여 구독자 자체의 오류를 처리 할 수도 있습니다.

정확한 의미 체계를 위해 웹에서 찾을 수있는 문서와 예제를 자세히 살펴 보거나 여기에서 특정 질문을 할 수 있습니다.

이것은 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하지만 응용 프로그램의 다른 부분에서 처리된다고 가정합니다.


RxJs 6을 사용하여 2019 년 5 월 업데이트

위에 제공된 답변에 동의하고, 명확성을 추가하기 위해 RxJs v6사용하여 장난감 데이터 및 간단한 약속 (setTimeout 포함)이 포함 된 구체적인 예제를 추가하고 싶습니다 .

Just update the passed id (currently hard-coded as 1) to something that does not exist to execute the error handling logic too. Importantly, also note the use of of with catchError message.

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

Output Data:

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 }

The key part, is equivalent to the following using plain promise control flow:

getPostById(1)
  .then(post => {
    return hydrateAuthor(post);
  })
  .then(post => {
    return dehydratePostTitle(post);
  })
  .then(author => {
    console.log(author);
  })
  .catch(err => {
    console.error(err);
  });

if getPromise function is in a middle of a stream pipe you should simple wrap it into one of functions mergeMap, switchMap or concatMap (usually mergeMap):

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

if you want to start your stream with getPromise() then wrap it into from function:

import {from} from 'rxjs';

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

As far as i just found out, if you return a result in a flatMap, it converts it to an Array, even if you returned a string.

But if you return an Observable, that observable can return a string;


If I understood correctly, you mean consuming the values, in which case you use sbuscribe i.e.

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

Additionally, you can just turn the observable to a promise using toPromise() as shown:

arrObservable.toPromise().then()

This is how I did it.

Previously

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

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

참고URL : https://stackoverflow.com/questions/34523338/rxjs-sequence-equivalent-to-promise-then

반응형