Лучшая практика для повторной попытки page.goto, page.waitForNavigation и т. Д. В кукловоде / JavaScript - PullRequest
0 голосов
/ 04 июля 2019

Я пытаюсь очистить некоторые веб-страницы в сети TOR, используя Puppeteer и пакет tor (apt install tor).Вероятно, из-за характера соединений TOR иногда я получаю тайм-аут.Кроме того, я новичок в асинхронном программировании на JavaScript.

Обычно у меня есть такая конструкция try-catch:

await Promise.all([
  page.goto(url),
  page.waitForNavigation({
    waitUntil: 'domcontentloaded'
  }),
]).catch((err) => { logMyErrors(err, true); });

или

let langMenu = await page.waitForXPath('//*[contains(@class, ".customer_name")]/ancestor::li').catch((err) => { logMyErrors(err, true); });

Но я думаю, что часто одна или несколько попыток помогут, наконец, получить нужный ресурс.Есть ли лучшая практика для выполнения повторных попыток?

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Я бы рекомендовал этот довольно простой подход:

async function retry(promiseFactory, retryCount) {
  try {
    return await promiseFactory();
  } catch (error) {
    if (retryCount <= 0) {
      throw error;
    }
    return await retry(promiseFactory, retryCount - 1);
  }
}

Эта функция вызывает promiseFactory и ожидает завершения возвращенного обещания. В случае возникновения ошибки процесс (рекурсивно) повторяется до тех пор, пока retryCount не достигнет 0.

Пример кода

Вы можете использовать функцию следующим образом:

await retry(
  () => page.waitForXPath('//*[contains(@class, ".customer_name")]/ancestor::li'),
  5 // retry this 5 times
);

Вы также можете передать любую другую функцию, возвращающую Promise, например Promise.all:

await retry(
  () => Promise.all([
    page.goto(url),
    page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  ]),
  1 // retry only once
);

Не совмещайте ожидание и улов

Еще один совет: вам не следует объединять await с .then или .catch, так как это приведет к неожиданным проблемам. Либо используйте await и окружите ваш код блоком try..catch, либо используйте .then и .catch. В противном случае ваш код может ожидать завершения работы функции catch и т. Д.

Вместо этого вы используете try..catch вот так:

try {
  // ...
} catch (error) {
  logMyErrors(error);
}
0 голосов
/ 04 июля 2019

Вот пример повторной попытки без пирамиды гибели. Хотя я не эксперт по ES, и могут быть некоторые новые функции async / await, которые могут сделать код чище, но пока это работает:

function retry(callback, retries) {
    let tries = 0;

    function tryRequest() {
        tries++;
        return callback().catch(e => {
            logMyErrors(e);

            if (tries < retries) {
                return tryRequest();
            }
        });
    }

    return tryRequest();
}

const logMyErrors = console.log;


retry(() => {
    console.log("retry");
    return new Promise((resolve, reject) => {
        //Emulate some rejections here
        if (Math.random() > 0.2) {
            throw new Error("Something went wrong."); 
        }

        resolve("Success");
    });
}, 10).then((result) => {}, (rejected) => {});

Функция retry принимает обратный вызов, который должен возвращать обещание. callback выполняется до тех пор, пока Обещание не будет выполнено (разрешено, отклонено) или пока не будет достигнута сумма retries.

...