开发者问题收集

执行任务时返回可观察对象 - Firebase 函数

2022-05-07
302

我的 Firebase Cloud Functions 中有一个函数需要运行抓取任务。当该函数正在抓取公共网站时,我需要向消费者返回进度反馈。 (例如“现在是 10%”、“现在是 30%”等等...)。

我想使用 Observables,但我得到了奇怪的结果,因为抓取函数是异步的,但它应该立即返回可观察对象。

这是我的 Firebase Functions Backend 的简化代码:

exports.scrapeFunction = functions.region('europe-west2').https.onCall((data) => {
 const res$ = new BehaviorSubject<Response>({
  completion: 0,
  completionMsg: 'One moment...',
  error: undefined,
  user: undefined,
 });
 scrape(data.id, res$);
 return res$.asObservable();
});

const scrape = async (id: number, res$: BehaviorSubject<Response>) => {

 const browser = await puppeteer.launch({ headless: true });
 const page = await browser.newPage();

 res$.next({
  completion: 10,
  completionMsg: 'I\'m loading the browser...'
 })

 await page.goto('https://example.com');

 res$.next({
  completion: 20,
  completionMsg: 'Page opened...'
 })

 /* Other scraping async functions... */

}

在我的前端,我有一个类似这样的代码:

response$ = Observable<Response> | undefined;

ngOnInit(): void {
 const scrapeFunction= getFunctions(undefined, 'europe-west2');
 const verifyClient = httpsCallable(functions, 'scrapeFunction');
 const { data } = await scrapeFunction({ id: 0001 });
 console.log('Data is', data);
 this.response$ = (data as any).source as Observable<Response>;
 this.response$.subscribe((res) => console.log(res));
 return;
}

登录控制台我得到:

Data is 
 {source: {...}}

 source:
  closed: false
  currentObservers: null
  hasError: false
  isStopped: false
  observers: []
  thrownError: null
  _value: {completion: 0, completionMsg: 'One moment...'}
  [[Prototype]]: Object
 [[Prototype]]: Object

我还收到以下错误,因为该函数没有返回我所知道的 Observable,而是一个具有第一个键“source”的对象。

ERROR Error: Uncaught (in promise): TypeError: _this.response$.subscribe is not a function
TypeError: _this.response$.subscribe is not a function
    at dashboard.component.ts:35:29
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:25:1)
    at ZoneDelegate.invoke (zone.js:372:1)
    at Object.onInvoke (core.js:28692:1)
    at ZoneDelegate.invoke (zone.js:371:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1276:1
    at ZoneDelegate.invokeTask (zone.js:406:1)
    at resolvePromise (zone.js:1213:1)
    at zone.js:1120:1
    at asyncGeneratorStep (asyncToGenerator.js:6:1)
    at _next (asyncToGenerator.js:25:1)
    at ZoneDelegate.invoke (zone.js:372:1)
    at Object.onInvoke (core.js:28692:1)
    at ZoneDelegate.invoke (zone.js:371:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1276:1
    at ZoneDelegate.invokeTask (zone.js:406:1)

有人能理解这种行为吗?



[ 更新 1 ]

我在 Firebase Functions 中将我的 index.ts 文件编辑成如下形式:

const res$ = new BehaviorSubject<Response>({
 completion: 0,
 completionMsg: 'One moment...',
});
exports.verifyClient = functions.https.onCall((data) => {
 scrapeCheck(data.id);
 return verifyClientRes$.asObservable();
});

const scrapeCheck = async (id: number) => {
 await scrape(id, res$);
 res$.complete();
 return;
};

/* The scrape function is actually on an external file */
const scrape = (/* ... */) => {
 /* ... */
}

** 但是我仍然在控制台上收到相同的错误。





[ 更新 2 ]

我创建了一个 StackBlitz 项目。您可以在这里找到它: https://stackblitz.com/edit/wherescrypto?file=functions/src/index.ts

导致此错误的函数位于“functions/src/index.ts”中,名为“verifyClient”

2个回答

查看一些可能有助于解决您遇到的问题的文档,我想知道您是否已经考虑过尝试使用 实时数据库firestore

您必须将更新推送到某个地方,客户端应用程序可以通过这种方式查看它们。此解决方案在此 stackoverflow 问题 的评论中提到。

Jose German Perez Sanchez
2022-05-14

将 scrape 放在 setTimeout 中即可解决问题。这样 scrape 就会在返回 observable 后被调用。

exports.scrapeFunction = functions.region('europe-west2').https.onCall((data) => {
 const res$ = new BehaviorSubject<Response>({
  completion: 0,
  completionMsg: 'One moment...',
  error: undefined,
  user: undefined,
 });
 setTimeout(() => scrape(data.id, res$));
 return res$.asObservable();
});
Aakash Garg
2022-05-07