[英]Async in RxJS Observable
First time with RxJS. 第一次使用RxJS。 Basically, I'm trying to make a twitter scraper which retrieves tweets from a query string. 基本上,我正在尝试制作一个Twitter抓取程序,以从查询字符串中检索推文。 The search url allows specifying of a min_position parameter which can be the last id of the previous search to sort of paginate. 搜索网址允许指定min_position参数,该参数可以是上一次进行分页的搜索的最后一个id。
The process kind of looks like this (where it loops back at the end): 该过程看起来像这样(最后循环返回):
get page -> next() each scraped tweet -> set min_position -> get page (until !has_more_items)
Requesting the page returns a promise and so I somehow have to wait until this is completed until I can proceed. 请求页面会返回一个承诺,因此我必须以某种方式等到完成为止才能继续。 I was hoping to pass an async function to Observable.create()
but that doesn't seem to work, it's only called a single time. 我希望将异步函数传递给Observable.create()
但这似乎不起作用,它仅被称为一次。
EDIT 编辑
I've had a play around after reading your resources as best as I could. 尽我所能阅读您的资源后,我一直在玩耍。 I came up with the following abstraction of my problem. 我提出了以下问题的抽象概念。
import { from, Observable } from 'rxjs'
import { concatMap, map, switchMap } from 'rxjs/operators'
let pageNumber = 0
const PAGE_SIZE = 3, MAX_PAGES = 3
async function nextPage() {
if (pageNumber >= MAX_PAGES) {
throw new Error('No more pages available')
}
await new Promise(res => setTimeout(res, 500)) // delay 500ms
const output = []
const base = pageNumber++ * PAGE_SIZE
for (let i = 0; i < PAGE_SIZE; i++) {
output.push(base + i)
}
return output
}
function parseTweet(tweet: number): string {
// simply prepend 'tweet' to the tweet
return 'tweet ' + tweet
}
const getTweets = (): Observable<string> => {
return from(nextPage()) // gets _html_ of next page
.pipe(
concatMap(page => page), // spreads out tweet strings in page
map(tweet => parseTweet(tweet)), // parses each tweet's html
switchMap(() => getTweets()) // concat to next page's tweets
// stop/finish observable when getTweets() observable returns an error
)
}
getTweets()
.subscribe(val => console.log(val))
It's quite close to working but now whenever nextPage()
returns a rejected promise, the entire observable breaks (nothing logged to the console). 它已经接近工作了,但是现在无论何时nextPage()
返回被拒绝的承诺,整个可观察的中断(什么都不会记录到控制台)。
I've attempted inserting a catchError
after the pipe
to finish the observable instead of running through and throwing an error but I can't get it to work. 我试图在pipe
后插入catchError
来完成可观察的操作,而不是通过运行并抛出错误,但是我无法使其正常工作。
Also this implementation is recursive which I was hoping to avoid because it's not scalable. 同样,该实现是递归的,我希望避免这种实现,因为它不可扩展。 I don't know how many tweets/pages will be processed in the observable in future. 我不知道将来会观察到多少条推文/页面。 It also seems that tweets from all 3 pages must be processed before the observable starts emitting values which of course is not how it should work. 似乎还必须处理所有3页上的推文,然后可观察对象开始发出值,这当然不是它应该如何工作的。
Thanks for your help! 谢谢你的帮助! :) :)
We need to loadTwits until some condition and somehow work with Promise? 我们需要加载Twits,直到某些情况出现并以某种方式与Promise合作? Take a look at example: 看一个例子:
function loadTwits(id) {
// Observable that replay last value and have default one
twitId$ = new BehaviorSubject(id);
return twitId$.pipe(
// concatMap - outside stream emit in order inner do
// from - convert Promise to Observable
concatMap(id => from(fetchTwits(id))),
map(parseTwits),
// load more twits or comlete
tap(twits => getLastTwitId(twits) ? twitId$.next(getLastTwitId(twits)) : twitId$.complete())
)
}
I figured it out after looking further into expand
and realising it was recursion that I needed in my observable. 在进一步研究expand
并意识到可观察到的递归之后,我弄清楚了。 This is the code that creates the observable: 这是创建可观察对象的代码:
const nextPage$f = () => from(nextPage()) // gets _html_ of next page
.pipe(
concatMap(page => page), // spreads out tweet strings in page
map(tweet => parseTweet(tweet)) // parses each tweet's html
)
const tweets$ = nextPage$f()
.pipe(
expand(() => morePages() ? nextPage$f() : empty())
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.