I’m new to JavaScript and to the Puppeteer Library. I know what async programming is and I’ve been playing around with it in JavaScript but when I try to use it with Puppeteer I get errors.
This is my index.js
file:
(async () => {
const browser = await puppeteer.launch({
headless: false,
});
// Disable Facebook notifications
const context = await browser.defaultBrowserContext();
await context.overridePermissions('https://www.facebook.com', ['geolocation', 'notifications']);
const page = await browser.newPage();
const logger = await myLog.logger;
console.log('About to use function.');
await utils.webAction(
logger, 'Enter Facebook',
(() => (page.goto(constants.linkHomepage, {waitUntil: ['domcontentloaded', 'networkidle2']})))
);
console.log('Content loaded');
// User Login
await utils.webAction(
logger, 'Wait for loginEmail element',
(() => (page.waitForSelector(constants.cssLoginEmail, {visible: true}))),
);
// Using a fake (incorrect) xpath to trigger the "catch" block
await utils.webAction(
logger, 'Enter user email using incorrect xpath', utils.webType,
page, "Fake user", "fake xpath/css" // Arguments of `webType`
);
})();
And the utils.js
file:
module.exports = {
webAction: function (logger, msg, func, ...args)
{
return new Promise((resolve) => {
let actionMessage, actionLevel;
try
{
func(...args);
actionMessage = msg;
actionLevel = 'info';
}
catch (error)
{
actionMessage = "Error while executing " + msg + " function.n---n" + error.message + "n---";
actionLevel = 'error';
}
finally
{
logger.log({
level: actionLevel,
message: actionMessage
})
}
console.log('Inside resolve Promise');
resolve('Fullfilment value of Promise');
console.log('Last line if resolve body');
})
},
// Type into input field
webType: function (page, text, xpath)
{
page.type(
xpath, text,
{delay: getRandomIntInRange(constants.minSpeed, constants.maxSpeed)}
);
},
};
I created the webAction
function because I want the code to look cleaner and also to keep track of where the program crashes (I’m open to other ways to achieve this).
This is the error I’m getting:
About to use function.
info: Enter Facebook {"timestamp":"2024-01-18 16:43:59"}
Inside resolve Promise
Last line if resolve body
Content loaded
info: Wait for loginEmail element {"timestamp":"2024-01-18 16:43:59"}
Inside resolve Promise
Last line if resolve body
info: Enter user email using incorrect xpath {"timestamp":"2024-01-18 16:43:59"}
Inside resolve Promise
Last line if resolve body
/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:85
this._reject(callback, new Errors_js_1.TargetCloseError('Target closed'));
TargetCloseError: Protocol error (Runtime.callFunctionOn): Target closed
at CallbackRegistry.clear (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:85:36)
at CdpCDPSession._onClosed (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/CDPSession.js:113:25)
at Connection.onMessage (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Connection.js:132:25)
at WebSocket.<anonymous> (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/node/NodeWebSocketTransport.js:52:32)
at callListener (/home/user/Projects/Javascript/Facebook/node_modules/ws/lib/event-target.js:290:14)
at WebSocket.onMessage (/home/user/Projects/Javascript/Facebook/node_modules/ws/lib/event-target.js:209:9)
at WebSocket.emit (node:events:514:28)
at Receiver.receiverOnMessage (/home/user/Projects/Javascript/Facebook/node_modules/ws/lib/websocket.js:1192:20)
at Receiver.emit (node:events:514:28)
at Receiver.dataMessage (/home/user/Projects/Javascript/Facebook/node_modules/ws/lib/receiver.js:560:14) {
cause: ProtocolError
at <instance_members_initializer> (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:96:14)
at new Callback (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:100:16)
at CallbackRegistry.create (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:32:26)
at Connection._rawSend (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Connection.js:91:26)
at CdpCDPSession.send (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/CDPSession.js:78:33)
at next (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-extra-plugin-stealth/evasions/sourceurl/index.js:34:41)
at CdpCDPSession.send (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-extra-plugin-stealth/evasions/sourceurl/index.js:75:16)
at #evaluate (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/ExecutionContext.js:211:50)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async ExecutionContext.evaluateHandle (/home/user/Projects/Javascript/Facebook/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/ExecutionContext.js:178:16)
}
Node.js v18.17.0
I want to be able to use the webAction
function so if you have any idea of how I can fix the error you are more than welcome.
In the output it says that it entered Facebook correctly but this is not true, as as soon as the browser opened it closed immediately!
And obviously it doesn’t complete correctly the order statements (waitForSelector
& utils.webType
). Can someone explain me what is happening and how to be able to run this code without any error?
2
Answers
In your webAction function, you are calling these functions but you are not awaiting the resolution of these promises.
You need to change your utils.js file to await the result of the func call:
The rule with promises is that if you care about ordering, or the result (which you do, 99% of the time), you need to
await
the promise. Toawait
a promise from a function, the promise chain needs to be returned to the caller so it can be awaited there. Currently, yourwebAction
function doesn’tawait func()
.webType
doesn’t awaitpage.type()
.In effect, your script fires off dozens of concurrent promises without awaiting any results, so ordering is arbitrary and errors are virtually guaranteed–the browser closes along with all of the other operations.
While this provides the fix to your code, the design could be improved. One approach to logging your Puppeteer operations is to use a Proxy and forward every call to
page
:Output:
You should be able to integrate your custom logger here and adjust to taste. As with
page
, use a closure forlogger
so you don’t have to repeatedly pass the same parameters on each call.Now, if messing with the Puppeteer code isn’t to your liking, you could keep state in a closure and set the log message on a separate line:
The issue here is that once you bring in concurrency and
Promise.all
, it would be difficult to keep operations separate. But at least you don’t need to modify your Puppeteer code to add the log calls.All that said, for most scripts it’s sufficient to rely on Puppeteer’s stack traces and log them in per-function
catch
blocks. This has the stack trace and should give plenty of debugging info. But the answer here should still provide some food for thought.