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

Applying binary information to an image

Let's say I have an <img/>. The img is initially set with src='http://somelocation/getmypic'. Later on, there might be a need to change the content of the image based on some ajax call that returns binary data. However, this decision c ...

What's a quick way in Javascript to add a string to all elements in an array?

I'm working with an array = ["a", "b", "c"]; What I need to do is concatenate a string, let's say "Hello", to each value in this array. The desired output should look like this: ["Hello_a", "Hello_b", "Hello_c"] Is there a quicker way in java ...

Creating a fresh array by applying a filter and assigning keys

I am facing an issue with my array structure, it looks like this, [ [ "Show/Hide", "P000", "MAX-CT05 FVM2-", "S", 1532, -9.5929406005, null, null, ...

JavaScript can be used to append multiple array values within double brackets in Spring MVC

I am currently developing an application using Spring MVC with MongoDB. In the collection, I have retrieved multiple image name values: Ex: 1.jpg, 2.jpg, 3.jpg.... My Query: Now I need to place these values inside double brackets [[]] Ex : [["1.jpg"," ...

Guide on utilizing two separate collections to store different types of data for an application's users

I am looking to create a database collection similar to {username : "jack", password : "pass"} for storing doctors' login information. I believe I can achieve this during signup by implementing the following code: var Doctor = mongoose.model("doctor" ...

Tips for Storing Your Data Collection: A Guide on Different Storage Options

In the different parts of my app, I require access to a dataset containing timezones. Traditionally, I have stored such data in a class method like Timezone.all_zones (Ruby) for easy accessibility anywhere in my code. Is there a way to achieve this same ...

What causes the indexOf method to return -1 even when the values are present in the array?

Could someone explain why the indexOf() method returns -1 even though the values are present in the array? The includes() function also returns false for me. I feel like I must be missing something or forgetting a crucial detail. Any insights on why the ...

Can you point me in the direction of the Monaco editor autocomplete feature?

While developing PromQL language support for monaco-editor, I discovered that the languages definitions can be found in this repository: https://github.com/microsoft/monaco-languages However, I am struggling to locate where the autocompletion definitions ...

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 ...

Using JavaScript to determine the time it will take to download something

Hi everyone, I'm a beginner in javascript and I am currently working on finding the download time of a file. I have calculated the size of the file and divided it by the current time, but unfortunately, I am not getting the correct result. This is th ...

Angular promise fails to resolve after an extended period of waiting for a response

My application is designed to send GET requests to my node/express server. In order to monitor changes in the server code, I have implemented a setTimeout function. While the promise function on the client side initially functions properly for a short peri ...

Extracting HTML elements between tags in Node.js is a common task faced

Imagine a scenario where I have a website with the following structured HTML source code: <html> <head> .... <table id="xxx"> <tr> .. </table> I have managed to remove all the HTML tags using a library. Can you suggest w ...

Options for HTML technologies in an application designed for managing enterprise metadata

Challenge We are facing the decision of determining which technologies to adopt as we transition from a rich client Silverlight application to an HTML-based client that can accommodate a metadata driven approach. Situation Our enterprise has been using ...

Troubleshooting an issue with an AJAX request

Having trouble getting the HTML back from an AJAX call - works in FF but returns "null" in IE when using alert(result.html()); Here's the code, any suggestions? Thanks! The errors variable is also null in IE. It doesn't matter what element I u ...

Issue with Materialize Sidenav: Not functional on iOS devices including iPhones, functions correctly on all other devices

My Materialize Sidenav is functioning on all devices except for iPad and iPhone. If you want to check out the code, here is the link to the repository: repo. Take a look at index.html (line 44 down) and js/onloadSetup.js. I attempted adding this in onload ...

Unlimited scrolling: Fetching additional data via an Ajax request?

I am working on setting up a table with infinite scroll functionality to showcase user information such as name, address, and email. To accomplish this, I began by importing the json-server package and creating an API endpoint using fakerjs in a separate f ...

"Encountering an issue when trying to choose a value from a select list using jQuery and the

What am I missing here or what is the correct approach to solve this? Take a look at the following code snippet: code snippet $(document).ready(function() { $(".metric_div").hide(); $("#solid_radio").click(function() { $("#solid").show(); ...

Is there a way to confirm the presence of multiple attributes in a JSON format using JavaScript?

Currently, I am developing a module that processes multiple complex JSON files and requires a method to notify users if certain elements are missing. Although the current approach works, I can't shake the feeling that there must be a more efficient a ...

When using Vue, the image element may not display the value stored in the object property

Excuse me for being new to Vue and struggling a bit. I'm attempting to display an image using a src that comes from an object property, which is retrieved from an array returned by data() I am not receiving any errors, but the icon is not appearing. ...

Utilizing an array for assigning values in conditional statements

I have been experimenting with toggling a CSS style on a couple of elements using an if statement. Currently, I have it working with several if statements, but I believe it can be simplified by utilizing an array with the values and just one if statement. ...