Dengan menggunakan Http, kami memanggil metode yang melakukan panggilan jaringan dan mengembalikan http yang dapat diamati:
getCustomer() {
return this.http.get('/someUrl').map(res => res.json());
}
Jika kami menerima hal ini dan menambahkan beberapa pelanggan ke dalamnya:
let network$ = getCustomer();
let subscriber1 = network$.subscribe(...);
let subscriber2 = network$.subscribe(...);
Apa yang ingin kami lakukan adalah memastikan bahwa ini tidak menyebabkan beberapa permintaan jaringan.
Ini mungkin tampak seperti skenario yang tidak biasa, tetapi sebenarnya cukup umum: misalnya jika pemanggil berlangganan yang diamati untuk menampilkan pesan kesalahan, dan meneruskannya ke templat menggunakan pipa async, kami sudah memiliki dua pelanggan.
Apa cara yang benar untuk melakukan itu di RxJs 5?
Yaitu, ini tampaknya berfungsi dengan baik:
getCustomer() {
return this.http.get('/someUrl').map(res => res.json()).share();
}
Tetapi apakah ini cara idiomatis untuk melakukan ini di RxJs 5, atau haruskah kita melakukan sesuatu yang lain?
Catatan: Sesuai sudut 5 baru HttpClient
, .map(res => res.json())
bagian dalam semua contoh sekarang tidak berguna, karena hasil JSON sekarang diasumsikan secara default.
sumber
Jawaban:
Tembolok data dan jika tersedia di-cache, kembalikan ini jika tidak buat permintaan HTTP.
Contoh plunker
Artikel ini https://blog.thoughtram.io/angular/2018/03/05/advanced-caching-with-rxjs.html adalah penjelasan yang bagus tentang cara melakukan cache
shareReplay
.sumber
do()
sebaliknya untukmap()
tidak mengubah acara. Anda dapat menggunakanmap()
juga tetapi kemudian Anda harus memastikan nilai yang benar dikembalikan pada akhir panggilan balik..subscribe()
tidak memerlukan nilai Anda dapat melakukannya karena mungkin sajanull
(tergantung pada apa yangthis.extractData
kembali), tetapi IMHO ini tidak mengekspresikan maksud kode dengan baik.this.extraData
berakhir sepertiextraData() { if(foo) { doSomething();}}
sebaliknya, hasil dari ekspresi terakhir dikembalikan yang mungkin bukan yang Anda inginkan.if (this.observable) { return this.observable; } else { this.observable = this.http.get(url) .map(res => res.json().data); return this.observable; }
Per saran Kristen, ini adalah salah satu cara yang bekerja dengan baik untuk HTTP yang dapat diamati, yang hanya memancarkan satu kali dan kemudian mereka menyelesaikan:
sumber
share
operator mungkin merupakan pilihan yang masuk akal (walaupun dengan beberapa kasus tepi yang buruk). Untuk diskusi mendalam tentang opsi lihat bagian komentar di posting blog ini: blog.jhades.org/…publishLast().refCount()
tidak dapat dibatalkan, setelah semua langganan yang dapat diamati dikembalikan olehrefCount
telah dibatalkan, efek bersihnya adalah sumber yang dapat diamati akan berhenti berlangganan, membatalkannya jika itu adalah "info"UPDATE: Ben Lesh mengatakan rilis minor berikutnya setelah 5.2.0, Anda akan dapat memanggil shareReplay () untuk benar-benar cache.
SEBELUMNYA.....
Pertama, jangan gunakan share () atau publishReplay (1) .refCount (), mereka adalah sama dan masalah dengannya, adalah bahwa ia hanya berbagi jika koneksi dilakukan saat yang diamati aktif, jika Anda terhubung setelah selesai. , itu menciptakan lagi diamati, terjemahan, tidak benar-benar caching.
Birowski memberikan solusi tepat di atas, yaitu menggunakan ReplaySubject. ReplaySubject akan mem-cache nilai-nilai yang Anda berikan (bufferSize) dalam kasus kami 1. Ini tidak akan membuat share yang dapat diamati seperti baru () setelah refCount mencapai nol dan Anda membuat koneksi baru, yang merupakan perilaku yang tepat untuk caching.
Inilah fungsi yang dapat digunakan kembali
Inilah cara menggunakannya
Di bawah ini adalah versi lebih lanjut dari fungsi cacheable. Yang satu ini memungkinkan memiliki tabel pencarian sendiri + kemampuan untuk menyediakan tabel pencarian kustom. Dengan cara ini, Anda tidak perlu memeriksa this._cache seperti pada contoh di atas. Perhatikan juga bahwa alih-alih meneruskan observable sebagai argumen pertama, Anda melewatkan fungsi yang mengembalikan observable, ini karena Http Angular dieksekusi segera, jadi dengan mengembalikan fungsi malas yang dieksekusi, kita dapat memutuskan untuk tidak memanggilnya jika sudah ada di cache kami.
Pemakaian:
sumber
const data$ = this._http.get('url').pipe(cacheable()); /*1st subscribe*/ data$.subscribe(); /*2nd subscribe*/ data$.subscribe();
? Jadi berperilaku lebih seperti operator lain ..rxjs 5.4.0 memiliki metode shareReplay baru .
Penulis secara eksplisit mengatakan "ideal untuk menangani hal-hal seperti caching hasil AJAX"
rxjs PR # 2443 feat (shareReplay): menambahkan
shareReplay
varianpublishReplay
sumber
menurut artikel ini
jadi di dalam jika pernyataan hanya menambahkan
untuk
.map(...)
sumber
rxjs versi 5.4.0 (2017-05-09) menambahkan dukungan untuk shareReplay .
Anda dapat dengan mudah memodifikasi layanan sudut untuk menggunakan ini dan mengembalikan yang dapat diamati dengan hasil yang di-cache yang hanya akan membuat panggilan http sekali saja (dengan asumsi panggilan pertama berhasil).
Contoh Layanan Sudut
Berikut adalah layanan pelanggan yang sangat sederhana yang digunakan
shareReplay
.customer.service.ts
Perhatikan bahwa penugasan dalam konstruktor dapat dipindahkan ke metode
getCustomers
tetapi karena dapat diamati kembali dariHttpClient
yang "dingin" melakukan hal ini dalam konstruktor dapat diterima karena panggilan http hanya akan dibuat dengan setiap panggilan pertama kesubscribe
.Juga asumsi di sini adalah bahwa data yang dikembalikan awal tidak basi dalam masa hidup contoh aplikasi.
sumber
Saya membintangi pertanyaan, tetapi saya akan mencoba dan mencoba ini.
Inilah buktinya :)
Hanya ada satu takeaway:
getCustomer().subscribe(customer$)
Kami tidak berlangganan respons api dari
getCustomer()
, kami berlangganan ReplaySubject yang dapat diobservasi yang juga dapat berlangganan ke Observable berbeda dan (dan ini penting) menyimpan nilai yang dipancarkan terakhir dan mempublikasikannya ke salah satu dari itu (ReplaySubject's ) pelanggan.sumber
Saya menemukan cara untuk menyimpan hasil http ke sessionStorage dan menggunakannya untuk sesi, sehingga tidak akan pernah memanggil server lagi.
Saya menggunakannya untuk memanggil API github untuk menghindari batas penggunaan.
FYI, batas sessionStorage adalah 5M (atau 4,75M). Jadi, tidak boleh digunakan seperti ini untuk set data yang besar.
------ sunting -------------
Jika Anda ingin memiliki data yang disegarkan dengan F5, yang menggunakan data memori bukan sesiStorage;
sumber
sessionStorage
, saya akan menggunakannya hanya untuk data yang diharapkan konsisten untuk seluruh sesi.getCustomer
dalam contohnya. ;) Jadi hanya ingin memperingatkan beberapa orang yang mungkin tidak melihat risikonya :)Implementasi yang Anda pilih akan bergantung pada apakah Anda ingin berhenti berlangganan () untuk membatalkan permintaan HTTP Anda atau tidak.
Bagaimanapun, dekorator TypeScript adalah cara yang bagus untuk perilaku standardisasi. Ini yang saya tulis:
Definisi dekorator:
sumber
Property 'connect' does not exist on type '{}'.
dari garisreturnValue.connect();
. Bisakah Anda menguraikan?Data Respons HTTP Cacheable menggunakan Rxjs Observer / Observable + Caching + Subscription
Lihat Kode Di Bawah Ini
* Penafian: Saya baru mengenal rxjs, jadi ingatlah bahwa saya mungkin menyalahgunakan pendekatan observable / observer. Solusi saya murni konglomerasi dari solusi lain yang saya temukan, dan merupakan konsekuensi dari kegagalan untuk menemukan solusi sederhana yang terdokumentasi dengan baik. Jadi saya memberikan solusi kode lengkap saya (seperti yang ingin saya temukan) dengan harapan dapat membantu orang lain.
* perhatikan, pendekatan ini secara longgar didasarkan pada GoogleFirebaseObservables. Sayangnya saya kurang pengalaman / waktu yang tepat untuk meniru apa yang mereka lakukan di bawah tenda. Tetapi berikut ini adalah cara sederhana untuk menyediakan akses tidak sinkron ke beberapa data yang bisa di-cache.
Situasi : Komponen 'daftar produk' bertugas menampilkan daftar produk. Situs ini adalah aplikasi web satu halaman dengan beberapa tombol menu yang akan 'memfilter' produk yang ditampilkan pada halaman.
Solusi : Komponen "berlangganan" ke metode layanan. Metode layanan mengembalikan array objek produk, yang diakses komponen melalui panggilan balik berlangganan. Metode layanan membungkus aktivitasnya dalam Pengamat yang baru dibuat dan mengembalikan pengamat. Di dalam pengamat ini, ia mencari data yang di-cache dan meneruskannya kembali ke pelanggan (komponen) dan kembali. Kalau tidak, ia mengeluarkan panggilan http untuk mengambil data, berlangganan respons, di mana Anda dapat memproses data itu (misalnya memetakan data ke model Anda sendiri) dan kemudian meneruskan data kembali ke pelanggan.
Kode
product-list.component.ts
product.service.ts
product.ts (model)
Ini adalah contoh dari output yang saya lihat ketika saya memuat halaman di Chrome. Perhatikan bahwa pada muatan awal, produk diambil dari http (panggilan ke layanan simpul saya, yang berjalan secara lokal pada port 3000). Ketika saya klik untuk menavigasi ke tampilan 'disaring' dari produk, produk ditemukan dalam cache.
Log Chrome (konsol) saya:
... [mengklik tombol menu untuk memfilter produk] ...
Kesimpulan: Ini adalah cara paling sederhana yang saya temukan (sejauh ini) untuk mengimplementasikan data respons http yang dapat di-cache. Di aplikasi sudut saya, setiap kali saya menavigasi ke tampilan produk yang berbeda, komponen daftar produk memuat ulang. ProductService tampaknya merupakan contoh bersama, jadi cache lokal 'produk: Produk []' di ProductService dipertahankan selama navigasi, dan panggilan berikutnya ke "GetProducts ()" mengembalikan nilai yang di-cache. Satu catatan terakhir, saya sudah membaca komentar tentang bagaimana observable / langganan perlu ditutup ketika Anda selesai untuk mencegah 'kebocoran memori'. Saya belum memasukkan ini di sini, tapi itu sesuatu yang perlu diingat.
sumber
Saya berasumsi bahwa @ ngx-cache / core dapat berguna untuk memelihara fitur caching untuk panggilan http, terutama jika panggilan HTTP dilakukan pada platform browser dan server .
Katakanlah kita memiliki metode berikut:
Anda dapat menggunakan
Cached
dekorator @ ngx-cache / core untuk menyimpan nilai yang dikembalikan dari metode membuat panggilan HTTP dicache storage
( yangstorage
dapat dikonfigurasi, silakan periksa implementasinya di ng-seed / universal ) - tepat pada eksekusi pertama. Kali berikutnya metode dipanggil (tidak masalah di browser atau platform server ), nilainya diambil daricache storage
.Ada juga kemungkinan untuk metode penggunaan caching (
has
,get
,set
) menggunakan caching API .anyclass.ts
Berikut adalah daftar paket, baik untuk caching sisi klien dan sisi server:
sumber
rxjs 5.3.0
Saya belum senang dengan itu
.map(myFunction).publishReplay(1).refCount()
Dengan banyak pelanggan,
.map()
dijalankanmyFunction
dua kali dalam beberapa kasus (saya berharap ini hanya dijalankan sekali). Tampaknya satu perbaikanpublishReplay(1).refCount().take(1)
Hal lain yang dapat Anda lakukan adalah tidak menggunakan
refCount()
dan langsung membuat Observable hot:Ini akan memulai permintaan HTTP terlepas dari pelanggan. Saya tidak yakin apakah berhenti berlangganan sebelum HTTP GET selesai akan membatalkannya atau tidak.
sumber
Favorit pribadi saya adalah menggunakan
async
metode untuk panggilan yang membuat permintaan jaringan. Metode itu sendiri tidak mengembalikan nilai, sebaliknya mereka memperbaruiBehaviorSubject
dalam layanan yang sama, komponen mana yang akan berlangganan.Sekarang Mengapa menggunakan
BehaviorSubject
bukanObservable
? Karena,onnext
.getValue()
metode ini.Contoh:
customer.service.ts
Lalu, di mana pun diperlukan, kita bisa berlangganan saja
customers$
.Atau mungkin Anda ingin menggunakannya secara langsung dalam templat
Jadi sekarang, sampai Anda membuat panggilan lagi ke
getCustomers
, data disimpan dicustomers$
BehaviorSubject.Jadi bagaimana jika Anda ingin menyegarkan data ini? lakukan panggilan ke
getCustomers()
Menggunakan metode ini, kami tidak perlu secara eksplisit menyimpan data antara panggilan jaringan berikutnya karena ditangani oleh Internet
BehaviorSubject
.PS: Biasanya ketika komponen dihancurkan itu adalah praktik yang baik untuk menyingkirkan langganan, untuk itu Anda dapat menggunakan metode yang disarankan dalam jawaban ini .
sumber
Jawaban yang bagus
Atau Anda bisa melakukan ini:
Ini dari versi terbaru rxjs. Saya menggunakan versi 5.5.7 dari RxJS
sumber
Panggil saja share () setelah peta dan sebelum berlangganan .
Dalam kasus saya, saya memiliki layanan generik (RestClientService.ts) yang melakukan panggilan sisanya, mengekstraksi data, memeriksa kesalahan dan mengembalikan yang dapat diamati ke layanan implementasi konkret (misalnya: ContractClientService.ts), akhirnya implementasi konkret ini mengembalikan diamati ke de ContractComponent.ts, dan yang satu ini berlangganan untuk memperbarui tampilan.
RestClientService.ts:
ContractService.ts:
ContractComponent.ts:
sumber
Saya menulis kelas cache,
Semuanya statis karena bagaimana kita menggunakannya, tetapi merasa bebas untuk menjadikannya kelas normal dan layanan. Saya tidak yakin apakah angular menyimpan satu instance untuk sepanjang waktu (baru di Angular2).
Dan inilah cara saya menggunakannya:
Saya berasumsi mungkin ada cara yang lebih pintar, yang akan menggunakan beberapa
Observable
trik tapi ini baik-baik saja untuk tujuan saya.sumber
Cukup gunakan lapisan cache ini, ia melakukan semua yang Anda butuhkan, dan bahkan mengelola cache untuk permintaan ajax.
http://www.ravinderpayal.com/blogs/12Jan2017-Ajax-Cache-Mangement-Angular2-Service.html
Ini sangat mudah digunakan
Lapisan (sebagai layanan sudut injeksi) adalah
sumber
Itu
.publishReplay(1).refCount();
atau.publishLast().refCount();
karena Angular Http dapat diamati selesai setelah permintaan.Kelas sederhana ini cache hasilnya sehingga Anda dapat berlangganan .nilai berkali-kali dan hanya membuat 1 permintaan. Anda juga dapat menggunakan .reload () untuk membuat permintaan baru dan menerbitkan data.
Anda bisa menggunakannya seperti:
dan sumbernya:
sumber
Anda dapat membangun kelas sederhana Cacheable <> yang membantu mengelola data yang diambil dari server http dengan beberapa pelanggan:
Pemakaian
Deklarasikan objek <> Cacheable (mungkin sebagai bagian dari layanan):
dan pawang:
Panggilan dari komponen:
Anda dapat memiliki beberapa komponen untuk berlangganan.
Lebih detail dan contoh kode ada di sini: http://devinstance.net/articles/20171021/rxjs-cacheable
sumber
Anda cukup menggunakan ngx-cacheable ! Ini lebih sesuai dengan skenario Anda.
Jadi, kelas layanan Anda akan menjadi seperti ini -
Inilah tautan untuk referensi lebih lanjut.
sumber
Sudahkah Anda mencoba menjalankan kode yang sudah Anda miliki?
Karena Anda membuat Observable dari janji yang dihasilkan dari
getJSON()
, permintaan jaringan dibuat sebelum siapa pun berlangganan. Dan janji yang dihasilkan dibagikan oleh semua pelanggan.sumber