Заключительная страница Puppeteer (Cluster), когда я с ней взаимодействую - PullRequest
1 голос
/ 07 мая 2019

В среде NodeJS v10.xx при попытке создать страницу PDF из некоторого HTML-кода у меня возникает проблема с закрытой страницей каждый раз, когда я пытаюсь что-то с ней сделать (setCacheEnabled, setRequestInterception и т. Д ...):

async (page, data) => {
  try {
    const {options, urlOrHtml} = data;
    const finalOptions = { ...config.puppeteerOptions, ...options };

    // Set caching flag (if provided)
    const cache = finalOptions.cache;
    if (cache != undefined) {
      delete finalOptions.cache;
      await page.setCacheEnabled(cache); //THIS LINE IS CAUSING THE PAGE TO BE CLOSED
    }

    // Setup timeout option (if provided)
    let requestOptions = {};
    const timeout = finalOptions.timeout;
    if (timeout != undefined) {
      delete finalOptions.timeout;
      requestOptions.timeout = timeout;
    }

    requestOptions.waitUntil = 'networkidle0';
    if (urlOrHtml.match(/^http/i)) {
      await page.setRequestInterception(true); //THIS LINE IS CAUSING ERROR DUE TO THE PAGE BEING ALREADY CLOSED
      page.once('request', request => {

        if(finalOptions.method === "POST" && finalOptions.payload !== undefined) {
          request.continue({method: 'POST', postData: JSON.stringify(finalOptions.payload)});
        }
      });

      // Request is for a URL, so request it
      await page.goto(urlOrHtml, requestOptions);
    }

    return await page.pdf(finalOptions);
  } catch (err) {
    logger.info(err);
  }
};

Я где-то читал, что эта проблема может быть вызвана отсутствием ожидания, но это не похоже на мой случай.

Я не использую непосредственно кукловод, ноэта библиотека, которая создает кластер поверх нее и обрабатывает процессы:

https://github.com/thomasdondorf/puppeteer-cluster

Ответы [ 2 ]

1 голос
/ 07 мая 2019

Вы уже дали решение, но так как это общая проблема с библиотекой (я - автор I), я хотел бы предоставить некоторые дополнительные сведения.

Как работает функция задачи

Когда задание ставится в очередь и готово к выполнению, puppeteer-cluster создаст страницу и вызовет функцию задачи (заданную для cluster.task) с созданным объектом page и данными в очереди.,Затем кластер ожидает, пока Обещание не будет выполнено (выполнено или отклонено), и закроет страницу и выполнит следующее задание в очереди.

Поскольку асинхронная функция неявно создает Обещание, это означает, что как толькоАсинхронная функция, заданная для функции cluster.task, завершена, страница закрыта.Никакой магии не происходит, чтобы определить, будет ли страница использоваться в будущем.

Ожидание асинхронных событий

Ниже приведен пример кода с распространенной ошибкой.Пользователь может захотеть дождаться внешнего события, прежде чем закрыть страницу, как в (не работающем) примере ниже:

Пример нерабочего (!) Кода:

await cluster.task(async ({ page, data }) => {
    await page.goto('...');
    setTimeout(() => { // user is waiting for an asynchronous event
        await page.evaluate(/* ... */); // Will throw an error as the page is already closed
    }, 1000);
});

В этом коде страница уже закрыта до выполнения асинхронной функции.Чтобы исправить способ сделать это, нужно вместо этого вернуть Обещание.

Пример рабочего кода:

await cluster.task(async ({ page, data }) => {
    await page.goto('...');

    // will wait until the Promise resolves
    await new Promise(resolve => {
        setTimeout(() => { // user is waiting for an asynchronous event
            try {
                await page.evalute(/* ... */);
                resolve();
            } catch (err) {
                // handle error
            }
        }, 1000);
    });
});

В этом примере кода функция задачи ждет, покаВнутреннее обещание разрешается до тех пор, пока оно не разрешит функцию.Это будет держать страницу открытой до тех пор, пока асинхронная функция не вызовет resolve.Кроме того, в коде используется блок try..catch, поскольку библиотека не может перехватывать события, генерируемые внутри блоков асинхронного кода.

1 голос
/ 07 мая 2019

Я понял.

Я действительно забыл об ожидании вызова, который был сделан для функции, которую я опубликовал.

Этот вызов был в другом файле, который я использую для создания экземпляра кластера:

async function createCluster() {
  //We will protect our app with a Cluster that handles all the processes running in our headless browser
  const cluster = await Cluster.launch({
    concurrency: Cluster[config.cluster.concurrencyModel],
    maxConcurrency: config.cluster.maxConcurrency
  });

  // Event handler to be called in case of problems
  cluster.on('taskerror', (err, data) => {
    console.log(`Error on cluster task... ${data}: ${err.message}`);
  });

  // Incoming task for the cluster to handle
  await cluster.task(async ({ page, data }) => {
    main.postController(page, data); // <-- I WAS MISSING A return await HERE
  });

  return cluster;
}
...