Запуск этого кода:
'use strict';
const puppeteer = require('puppeteer');
main().then(() => {
console.log('Done');
});
async function main() {
process.on('unhandledRejection', r => {
console.error(r);
process.exit(1);
});
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.google.com/', {
waitUntil: 'networkidle'
});
await page.waitForSelector('#i-dont-exist', {
timeout: 500
});
}
Произведет трассировку стека:
Error: waiting failed: timeout 500ms exceeded
at Timeout.WaitTask._timeoutTimer.setTimeout (/Users/iftachbar/work/impactlabs/scrapper/node_modules/puppeteer/lib/FrameManager.js:410:58)
at ontimeout (timers.js:488:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:283:5)
Что очень затрудняет отладку случая.
Я ожидаю, что интуитивно понятная трассировка трека должна включать строку, в которой я запускаю page.waitForSelector()
.
Может быть, есть другое решение, о котором я не думаю...
FWIW, родительские функции, которые устанавливают обратные вызовы тайм-аута, также исключаются из трассировки стека:
'use strict';
main();
function main() {
setTimeout(() => { throw new Error('Error in a timeout'); }, 500);
}
Error: Error in a timeout
at Timeout.setTimeout [as _onTimeout] (test.js:6:28)
at ontimeout (timers.js:469:11)
at tryOnTimeout (timers.js:304:5)
at Timer.listOnTimeout (timers.js:264:5)
Я решил это с помощью патча для обезьян. Не лучшее решение, но оно необходимо, если у вас большая система и вам нужно отладить, где ожидание привело к ошибке:
async function catchTimeoutErrors(callback) {
const error = new Error('Got timeout');
try {
return await callback();
} catch (e) {
if (e.stack.indexOf('Timeout.WaitTask._timeoutTimer.setTimeout') >= 0) {
error.stack += '\nCaused by ' + e.stack;
throw error;
}
throw e;
}
}
async function patchPuppeteer() {
const browser = await puppeteer.launch({
headless: true
});
const page = await browser.newPage();
const mainFrame = page.mainFrame();
const originalWaitForFunction = mainFrame.__proto__.waitForFunction;
mainFrame.__proto__.waitForFunction = function waitForFunction() {
return catchTimeoutErrors(() => originalWaitForFunction.apply(this, arguments));
};
await browser.close();
}
Написал для версии 0.10.1, с 0.11.0 еще не тестировал.
@barnash есть несколько мест, где кукловод работает асинхронно и где трассировка стека может сбивать с толку.
Решением будет node.js, собирающий асинхронные трассировки стека; все другие решения не будут работать достаточно хорошо в общем случае.
Самый полезный комментарий
Я решил это с помощью патча для обезьян. Не лучшее решение, но оно необходимо, если у вас большая система и вам нужно отладить, где ожидание привело к ошибке:
Написал для версии 0.10.1, с 0.11.0 еще не тестировал.