جاوااسکریپت - Puppeteer منتظر صفحه بماند و setTimeout در توابع ابری Firebase کار نمیکند


من دارم تلاش میکنم یک صفحه را که روی فایربیس میزبانی میکنم بارگذاری کنم و با Puppeteer آن را به PDF تبدیل کنم.
با یک صفحه HTML ساده به خوبی کار میکند.
حالا دادهها را از فایربیس میگیرم و در صفحه نمایش میدهم، بنابراین نیاز دارم صبر کنم تا صفحه بهطور کامل بارگذاری شود قبل از اینکه بتوانم PDF را ایجاد کنم.
این در هنگام تست محلی با شبیهساز فایربیس npm run serve به خوبی کار میکند.
با این حال در Cloud Functions کار نمیکند، زمانسنج فقط منتظر میماند تا عملکرد منقضی شود.
لاگها نشان میدهند waitFor و پس از مدتی Function execution took 120002 ms, finished with status: 'timeout'.
من روشهای بسیاری امتحان کردم و نمیدانم چه کنم و دارم فکر میکنم که ممکن است این یک باگ در Cloud Functions باشد.

import * as functions from 'firebase-functions';
// tslint:disable-next-line:no-duplicate-imports
import { VALID_MEMORY_OPTIONS } from 'firebase-functions';
// import * as puppeteer from 'puppeteer';

const runtimeOpts = {
  timeoutSeconds: 120,
  memory: VALID_MEMORY_OPTIONS[4],
};

// const cors = require('cors')({ origin: true });

export const generatePDF = functions
  .runWith(runtimeOpts)
  // .region('europe-west1')
  .https.onRequest(async (request: any, response: any) => {
    // cors(request, response, async () => {
    console.log(7);

    const hostname = request.hostname;

    let url = '';
    if (hostname === 'localhost') {
      url = 'http://localhost:5000';
    } else {
      url = 'https://myapp.firebaseapp.com';
    }

    const puppeteer = require('puppeteer');

    console.log('launch puppeteer');

    const browser = await puppeteer.launch({
      args: ['--no-sandbox', '--disable-setuid-sandbox'],
    });
    // debug: {headless: false}

    console.log('new page');

    const page = await browser.newPage();

    console.log('goto');

    await page
      .goto(`${url}/report/A3p71Fl5GD98Sjks5BJg`)
      .catch((error: any) => {
        console.log(error);
        return response.send('Timeout1');
      });

    console.log('waitFor');

    // await page.waitFor(10000).catch((error: any) => {
    //   console.log(error);
    //   return response.send('Timeout2');
    // });

    await new Promise(resolve => setTimeout(resolve, 5000));

    // await page.waitForNavigation({
    //   waitUntil: 'networkidle0',
    // });

    // Wait for element to render
    // await page.waitForSelector('#end');

    // await page.waitFor(10000);

    console.log('create pdf');

    const pdf = await page.pdf({
      format: 'A4',
    });

    console.log('close browser');

    await browser.close();

    // response.setHeader('Content-Disposition', 'attachment; filename=customfilename.pdf');
    response.setHeader('Content-Disposition', 'filename=customfilename.pdf');
    return response.type('application/pdf').send(pdf);
    // });
  });

export const helloWorld = functions.https.onRequest(
  async (request: any, response: any) => {
    await new Promise(resolve => setTimeout(resolve, 5000));
    response.send('Hello from Firebase!');
  },
);

// function delay(ms: number) {
//   return new Promise(resolve => setTimeout(resolve, ms));
// }

package.json:

{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "8"
  },
  "main": "lib/index.js",
  "dependencies": {
    "cors": "^2.8.5",
    "firebase-admin": "^8.7.0",
    "firebase-functions": "^3.3.0",
    "puppeteer": "^2.0.0"
  },
  "devDependencies": {
    "tslint": "^5.12.0",
    "typescript": "^3.6.3"
  },
  "private": true
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017",
    "lib": ["dom"]
  },
  "compileOnSave": true,
  "include": ["src"]
}

پاسخ شماره ۱

شما باید puppeteer را خارج از تابع خود وارد کنید تا firebase بتواند از آن دوباره استفاده کند. همچنین نیازی نیست timeouts ایجاد کنید، اما میتوانید از await page.waitFor(5000); استفاده کنید. من سعی کردم یک مثال حداقلی برای مورد استفاده شما ایجاد کنم. اگر هنوز با خطا مواجه میشوید، لطفاً به کنسول توابع firebase خود بروید و لاگها را بررسی کنید، آنها باید نشان دهند مشکل چیست.

همچنین این احتمال وجود دارد که شما صورتحساب را در حساب Firebase خود فعال نکرده باشید - در این صورت عملکرد شما نمیتواند به میزبانهای شخص ثالث دسترسی پیدا کند.

import * as functions from 'firebase-functions';
import * as puppeteer from 'puppeteer';

export const generatePDF = functions
    .runWith({ timeoutSeconds: 30, memory: "1GB" })
    .https.onRequest(async (request, response) => {

        const url = 'https://www.yoururl.com/';
        const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });

        const page = await browser.newPage();
        await page.goto(url);
        await page.waitFor(5000);

        const pdf = await page.pdf({
            format: 'A4',
        });

        await browser.close();

        response.setHeader('Content-Disposition', 'filename=customfilename.pdf');
        return response.type('application/pdf').send(pdf);
    });