tjfdfs tjfdfs - 2 months ago 20
Javascript Question

Rx.js: get all results as array from promise lists

Here's the demo:



var availableNews$ = Rx.Observable.fromPromise(fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty').then(res => res.json()));
var pager$ = new Rx.Subject();
var fetchedNews$ = Rx.Observable.combineLatest(availableNews$, pager$, (news, pager) => news.slice((pager.current - 1) * pager.items_per_page, pager.items_per_page))
.flatMap(id => id)
.map(id => Rx.Observable.fromPromise(fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`).then(res => res.json()))
.concatMap(res => res));
pager$.subscribe(v => {
console.log(v);
document.querySelector('#pager').textContent = JSON.stringify(v);
});
fetchedNews$.subscribe(x => {
console.log(x);
document.querySelector('#news').textContent = JSON.stringify(x);;
})
pager$.next({current: 1, items_per_page: 10})

<script src="https://unpkg.com/@reactivex/rxjs/dist/global/Rx.js"></script>
pager: <p id="pager"></p>
news: <p id="news"></p>
<p>the news return obserable instance instead of <em>array of fetched news</em> which is desired</p>





I do these things in code:

1, fetch top news feeds (availableNews$)

2, use pager to limit which feeds should be fetch (pager$)

3, fetch news which should be a array (fetchedNews$)

But I stunk at step 3, the results returned is the stream of each promiseObserable, not result of each promise concated as a array.

Thanks for help ^_^

Answer

You have a combination of two or three errors.

First, the lambda in your combineLatest() does not operate on the array of news ids, but on a single item containing the array. The flatmap() is therefore unnecessary, as is the concatMap().

Second, you don't need to create Observable from fetch(), as

third, the combineLatest() you have does not give a Promise that is fullfilled when the news are loaded, but when the availableNews and pager are loaded. So you have to make a third Observable with combineLatest(), but from the Array of fetch() Promises. You then subscribe to that in your second subscribe(), like this:

var availableNews$ = Rx.Observable.fromPromise(fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty').then(res => res.json()));
var pager$ = new Rx.Subject();
var fetchedNews$ = Rx.Observable.combineLatest(availableNews$, pager$, (news, pager) =>
  Rx.Observable.combineLatest.apply(this, news.slice((pager.current - 1) * pager.items_per_page, pager.items_per_page)
    .map(id => fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`).then(res => res.json()))));

pager$.subscribe(v => {
  document.querySelector('#pager').textContent = JSON.stringify(v);
});

fetchedNews$.subscribe(x => { 
  x.subscribe(a => {
    document.querySelector('#news').textContent = JSON.stringify(a);
  });
})
pager$.next({current: 1, items_per_page: 10})
<script src="https://unpkg.com/@reactivex/rxjs/dist/global/Rx.js"></script>
pager: <p id="pager"></p>
news: <p id="news"></p>
<p>the news return obserable instance instead of <em>array of fetched news</em> which is desired</p>