Trình tự RxJS tương đương với promise.then ()?


83

Tôi đã từng phát triển rất nhiều với sự hứa hẹn và bây giờ tôi đang chuyển sang RxJS. Tài liệu của RxJS không cung cấp một ví dụ rõ ràng về cách chuyển từ chuỗi hứa hẹn sang chuỗi quan sát.

Ví dụ: tôi thường viết chuỗi hứa hẹn với nhiều bước, như

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

Tôi nên viết lại chuỗi lời hứa này theo kiểu RxJS như thế nào?

Câu trả lời:


80

Đối với luồng dữ liệu (tương đương với 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
  });

Một lời hứa có thể được chuyển đổi thành một lời hứa có thể quan sát được với Rx.Observable.fromPromise.

Một số toán tử hứa hẹn có một bản dịch trực tiếp. Ví dụ RSVP.all, hoặc jQuery.whencó thể được thay thế bằng Rx.Observable.forkJoin.

Hãy nhớ rằng bạn có một loạt các toán tử cho phép chuyển đổi dữ liệu không đồng bộ và thực hiện các tác vụ mà bạn không thể hoặc sẽ rất khó thực hiện với các hứa hẹn. Rxjs tiết lộ tất cả các quyền hạn của nó với chuỗi dữ liệu không đồng bộ (chuỗi tức là nhiều hơn 1 giá trị không đồng bộ).

Đối với quản lý lỗi, chủ đề này phức tạp hơn một chút.

  • có các toán tử bắtcuối cùng
  • retryWhen cũng có thể giúp lặp lại một chuỗi trong trường hợp lỗi
  • bạn cũng có thể đối phó với các lỗi trong chính người đăng ký bằng onErrorchức năng này.

Để biết ngữ nghĩa chính xác, hãy xem kỹ tài liệu và ví dụ mà bạn có thể tìm thấy trên web hoặc đặt các câu hỏi cụ thể tại đây.

Đây chắc chắn sẽ là một điểm khởi đầu tốt để đi sâu hơn vào quản lý lỗi với Rxjs: https://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html


Tôi luôn thấy chuỗi có thể quan sát được kết thúc bằng subscribe (). Vì đây chỉ là một chức năng của đối tượng quan sát được, có lý do gì để làm điều này? Nó có phải là chức năng để bắt đầu chuỗi không?
Haoliang Yu

chính xác như vậy. Nếu không có người quan sát nào chuyển qua đăng ký, người quan sát được của bạn sẽ không phát ra bất kỳ dữ liệu nào nên bạn sẽ không thấy bất kỳ luồng dữ liệu nào.
user3743222

7
Tôi khuyên bạn nên xem phần này: gist.github.com/staltz/868e7e9bc2a7b8c1f754 . CNTT có thể ngon miệng hơn tài liệu chính thức.
dùng3743222

3
Promise.thenhơn .flatMap.map.
Tamas Hegedus

1
FYI điều này không chính xác tương đương như trong các Promiselỗi phiên bản thứ 3 thensẽ được phát hiện bởi catch. Đây không phải là họ.
mik01aj

35

Một giải pháp thay thế hiện đại hơn:

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

Cũng lưu ý rằng để tất cả điều này hoạt động, bạn cần subscribe chuyển nó Observableở đâu đó, nhưng tôi cho rằng nó được xử lý trong một số phần khác của ứng dụng.


Tôi rất mới đối với RxJS, nhưng do chúng tôi chỉ xử lý luồng ban đầu của một sự kiện ở đây và mergeMap()do đó thực tế không có bất kỳ thứ gì để hợp nhất , tôi tin rằng chúng ta có thể đạt được điều tương tự trong trường hợp này bằng cách sử dụng concatMap()hoặc switchMap(). Tôi đã hiểu điều này chính xác chưa ...?
Dan King

8

Cập nhật tháng 5 năm 2019, sử dụng RxJs 6

Đồng ý với các câu trả lời được cung cấp ở trên, muốn thêm một ví dụ cụ thể với một số dữ liệu đồ chơi và lời hứa đơn giản (với setTimeout) bằng cách sử dụng RxJs v6 để thêm rõ ràng.

Chỉ cần cập nhật id đã truyền (hiện được mã hóa cứng là 1) thành một thứ không tồn tại để thực thi logic xử lý lỗi. Quan trọng, cũng cần lưu ý việc sử dụng ofvới catchErrortin nhắn.

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

Dữ liệu đầu ra:

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 }

Phần quan trọng, tương đương với phần sau bằng cách sử dụng luồng điều khiển hứa hẹn đơn giản:

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

0

nếu getPromisehàm nằm giữa đường ống luồng, bạn nên đơn giản bọc nó thành một trong các hàm mergeMap, switchMaphoặc concatMap(thường là mergeMap):

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

nếu bạn muốn bắt đầu luồng của mình bằng getPromise()thì hãy bọc nó vào fromchức năng:

import {from} from 'rxjs';

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

0

Theo như tôi vừa phát hiện ra, nếu bạn trả về một kết quả trong Bản đồ phẳng, nó sẽ chuyển đổi nó thành Mảng, ngay cả khi bạn trả về một chuỗi.

Nhưng nếu bạn trả về một Observable, thì có thể quan sát được đó có thể trả về một chuỗi;


0

Nếu tôi hiểu đúng, bạn có nghĩa là sử dụng các giá trị, trong trường hợp đó bạn sử dụng sbuscribe, tức là

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

Ngoài ra, bạn chỉ có thể chuyển lời hứa có thể quan sát được bằng cách sử dụng toPromise () như được hiển thị:

arrObservable.toPromise().then()

0

Đây là cách tôi đã làm điều đó.

Trước đây

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

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

tác dụng phụ : phát hiện thay đổi cũng bắt đầu hoạt động sau khi chuyển đổi lệnh gọi lại thành có thể quan sát được .
Anand Rockzz

0

Trình tự RxJS tương đương với promise.then ()?

Ví dụ

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");
        });
    });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.