Mistakes in my async/await workflow: How am I incorrectly loading and injecting this external script?

Encountering a simple problem: some calls to refresh() cause window.grecaptcha to become undefined. It doesn't happen all the time, probably due to network delays. Debugging this issue is proving to be tricky, especially since I'm still new to this concept. Any assistance or guidance on potential code errors would be greatly appreciated.

class RecaptchaController {
  loadPromise;

  async refresh() {
    await this.load();
    // window.grecaptcha is (sometimes) undefined in next line
    this.element.value = window.grecaptcha.execute(this.options.siteKey, { action: 'submit' });
  }

  load() {
    if (!this.loadPromise) {
      this.context.logDebugActivity('load');

      this.loadPromise = new Promise((resolve, reject) => {
        const url = new URL(this.options.apiUrl);
        url.searchParams.append('render', this.options.siteKey);
        url.searchParams.append('hl', this.options.locale);

        const script = document.createElement('script');
        script.setAttribute('id', this.identifier);
        script.setAttribute('src', url.toString());

        // The relevant part where I'm resolving the promise
        script.addEventListener('load', resolve);
        script.addEventListener('error', reject);

        document.body.appendChild(script);
      })
    }

    return this.loadPromise;
  }
}

An error message shows:

recaptcha_controller.js:27 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'execute')

In essence, refresh is intended to be called within a submit event handler, like so:

form.addEventListener('submit', async (e) => {
   // This is NOT how the initialization works!
   // Just an example about how I'm calling refresh()
   cost recaptchaController = new RecaptchaController();

   // This is actually what is happening
   await recaptchaController.refresh();

   // Handling submission using fetch()...
});

Omitting some code for clarity purposes and mentioning that there's a framework involved (though irrelevant).

Answer №1

After running a test with reCAPTCHA v3, I noticed that reCAPTCHA itself loads another locale-specific script asynchronously when checking the requests in the DevTools network panel. If you want to dynamically load reCAPTCHA the way you are attempting to, you will need to set up a globally visible callback and pass its name to the onload parameter of api.js.

  start() {
    if (!this.loadPromise) {
      this.logger.logDebugActivity('start');

      this.loadPromise = new Promise((resolve, reject) => {
        // recaptcha onload will resolve this promise
        window._recaptchaOnLoad = () => resolve();

        const url = new URL(this.config.apiUrl);
        url.searchParams.append('onload', '_recaptchaOnLoad');
        url.searchParams.append('render', this.config.siteKey);
        url.searchParams.append('hl', this.config.locale);

        const script = document.createElement('script');
        script.setAttribute('id', this.identifier);
        script.setAttribute('src', url.toString());

        // load event no longer needed
        script.addEventListener('error', reject);

        document.body.appendChild(script);
      }).finally(() => {
        // optionally delete window property
        delete window._recaptchaOnLoad;
      })
    }

    return this.loadPromise;
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Component failing to refresh with each key modification

My understanding is that adding a key attribute to a component should make it reactive when the key changes. However, with a v-navigation-drawer from Vuetify, this doesn't seem to have any impact. I've tried making arbitrary changes to the logge ...

Creating a personalized and versatile table component with unique functionality: where to begin?

Looking to create a versatile table component that can adapt for both desktop and mobile versions. If the screen width is below 720px, it should display a table using div, ul, li elements with a "load more" button at the bottom. If the screen width i ...

The final piece left in stitching together an array

Issue I have been struggling with this code for some time now and I can't seem to figure out the solution. I am receiving 3 values from inputs and trying to remove all the empty spaces "" in the array, but when I execute the code, it displays the foll ...

Retrieve a JSON object from a JSON array using a specific key

I am facing a situation where I have a json array containing multiple json objects. Let's take the example of a course object with the following structure: {"name": "Math", "unit": "3"} The json array looks something like this: [{"name": "Math", "u ...

Modifying the color of an empty string is not feasible in Javascript

Is it possible to change the color of FirstName only when there is text input? Currently, it turns green when there's text, but I want it to turn red when it's empty. How can this be achieved? $(document).on("click", '.btn-info.mailCo ...

Trigger a function following a collection update in Angular Meteor

I am in the process of developing a multiplayer game, and I would like a specific function to be triggered once the first player updates an "isStarted" field in the collection. Within my controller code: angular.module('mcitygame').directive(&a ...

Checking the conditional styling implemented with Material UI makeStyles: a step-by-step guide

For the past few weeks, I've been working on a React app where I heavily rely on Material UI components. One particular component changes its style based on the values of its props. To achieve this, I used the following approach: const useStyles = ...

Having trouble with your jQuery code not loading properly?

Currently working on debugging jQuery code and facing an issue where a particular section of the code is not being triggered upon clicking. The HTML/PHP in question: $cartlink .= "<a class='add_to_cart button' data-id='{$product->id} ...

JavaScript and Responsive Design Techniques

I'm struggling to create a responsive page that starts with a mobile-first approach, but I keep running into the same issue. When I have my dropdown menu in the mobile version, it looks good. However, when I try to switch it to the non-mobile version ...

Should you consider using the Singleton pattern in Node.js applications?

After stumbling upon this specific piece discussing the creation of a singleton in Node.js, it got me thinking. The require functionality according to the official documentation states that: Modules are cached after the first time they are loaded. Multi ...

Is there a method in CSS animations that allows for endlessly repeating successive animations in a specified sequence?

While working with CSS animations, I encountered a challenge of making two animations occur successively and repeat infinitely without merging keyframes. Is there a way to achieve this using only CSS? If not, how can I accomplish it using JavaScript? I a ...

When submitting an Asp net mvc Form, some fields may not be posted as expected

I'm facing an issue with my ASP.NET MVC application form. Some fields that should be posted are missing, and I can't figure out why. <form id="FormRegistroUsuario" action="/Account/AdminSecurityUserAccountAdd"> <fieldset> ...

Disappear notification with jQuery after a set amount of time

I stumbled upon this amazing script for displaying warning messages from this source: Within the script, it is configured to hide the warning message following a click event. $('.message').click(function(){ $(th ...

Troubleshooting: Images not displaying on webpage due to Ajax, JQuery, and JavaScript integration

I'm currently working on building a dynamic photo gallery using Ajax and JQuery in Javascript. I have set up a directory named "images" in Visual Studio Code and it contains my selection of 5 images. However, when I click the "next" and "previous" but ...

Using TypeScript with Node.js and Sequelize - the process of converting a value to a number and then back to a string within a map function using the OR

Currently, I am facing a challenge in performing addition on currency prices stored as an array of objects. The issue arises from the fact that the currency type can vary among 3 different types within the array of objects. The main hurdle I encounter is ...

I encountered an unexpected obstacle while reloading my Next.js application with animejs. The error message reads: "SyntaxError: Unexpected token 'export'." This unwelcome occurrence took place during the

Encountering an error with animejs when reloading my Next.js app: An unexpected token 'export' is causing a SyntaxError. This issue occurred during the page generation process. The error originates from file:///Users/.../node_modules/animejs/lib ...

Using canvas to smoothly transition an object from one point to another along a curved path

As a beginner in working with canvas, I am facing a challenge of moving an object from one fixed coordinate to another using an arc. While referring to the code example of a solar system on https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutori ...

What's causing the failure in the execution of the "Verify version" step?

Upon reviewing the 3DSecure GlobalPay documentation, my team decided to integrate it using JSON, incorporating our own client-side implementation. This decision was made because we already have another integration with a different 3DS verification service ...

What is causing express.js not to authenticate properly?

I'm currently in the process of developing a server application using node.js, which is up and running on localhost:8080. As I attempt to make a login request, I've encountered an issue where one method works while the other fails. My suspicion i ...

WebApp specifically designed for iPads that mimics the functionality of a swipe

I am in the process of developing a full-screen web application for an iPad that will showcase a series of images in a slider format. The users should be able to swipe between the images and click on one to view it in detail. Below is an example showcasin ...