How to handle asynchronous callbacks in puppeteer page evaluation

I’m struggling with executing a function that has a callback inside puppeteer’s evaluate method. The callback function doesn’t seem to trigger at all, even though I know the webpage is functioning correctly.

In the target webpage, there’s an event listener that works like this:

API.on('loaded').run(function(api){
  // API is ready to use here
})

I’m trying to capture this ready API instance using puppeteer:

try {
  const apiInstance = await page.evaluate('API.on("loaded")');
  const output = await page.evaluate(api => api.run, function(data) {
    console.log(JSON.stringify(data));
    // handle the callback data
  }, apiInstance);
    console.log('output', JSON.stringify(output));
} catch (error) {
    console.log('evaluation error', error);
} finally {
    console.log('done');
}

This approach isn’t working for me. Any suggestions on how to properly handle this callback pattern?

Promises work, but there’s a cleaner approach.

I’ve hit the same callback hell scraping data from tons of APIs. The real problem? You’re mixing browser and Node contexts - it gets messy quick.

Skip wrestling with Puppeteer’s evaluate limits. I automated this whole thing with Latenode. Set up a scenario that launches the browser, waits for the API, grabs callback data, and processes everything in one smooth flow.

Best part? Latenode handles all the async timing. No more Promise wrapping or context switching headaches. I use it for callback-heavy SPAs and it saves hours of debugging.

Trigger it on schedule or via webhook. It’ll retry if the API loads slow. Way more reliable than coordinating everything manually.

Use page.waitForFunction() instead of evaluate. It polls the page until the callback runs. Try await page.waitForFunction(() => window.apiReady) and set apiReady to true in your callback. Way easier than wrapping promises.

Your problem is that page.evaluate() runs synchronously in the browser and can’t handle async callbacks. You’re also mixing up the API call syntax.

You need to wrap the callback-based API in a Promise inside the evaluate function. Here’s how I fixed this:

const result = await page.evaluate(() => {
  return new Promise((resolve) => {
    API.on('loaded').run(function(api) {
      // Process your data here
      const processedData = api.someMethod();
      resolve(processedData);
    });
  });
});

This makes the evaluate method wait for the Promise to resolve when the callback fires. I use this pattern all the time with third-party APIs that use callbacks - works every time. Just check that the API exists on the page first or you’ll get reference errors.