Tips for ensuring that the callback method waits for the completion of Google Markers creation

While developing my app with the Google Maps library, I encountered an issue either due to an unexplainable delay in creating markers or an unseen asynchronous problem.

Here is a breakdown of the situation: The code retrieves the locations of Electric Charging Stations along a route between starting and ending points, creates Google markers for each station retrieved in JSON format, and adds them to an array. The intention is to later calculate a route with stopovers using these markers (not shown here).

The main problem is that it initiates the calculation method before completing the marker creation process.

To align the results, rather than fetching all the results at once, I implemented a loop that follows this sequence:

  1. Create a route and extract an encoded polyline from it (for use in the URL)
  2. Retrieve results
  3. Create markers, place them on the map, and add them to the array
  4. Console log completion message ('EV markers creation finished')

Subsequently, it triggers the route calculation process (here displayed as an alert 'calculateAndDisplayRoute method called')

However, in reality, the loop finishes and logs in the console, but the final markers are not created until after the alert is triggered, and only then can you see the markers appear on the map.

You can test out the following code snippet: https://codepen.io/reivilo85k/pen/wvowpab

Below is the problematic code segment (additional code was added to the CodePen example for proper functionality):

chargingPointsMarkers = [];
markerArray = [];

async callbackHandler(startEndPointsArray, calculateAndDisplayRoute): Promise<void> {
    await this.setChargingStationsMarkers();
    calculateAndDisplayRoute();
  }

function calculateAndDisplayRoute() {
      alert('calculateAndDisplayRoute method called')
    }

  async function setChargingStationsMarkers() {
const polylineMarkersArray = await createMarkersArray();
console.log('Polyline Markers created', polylineMarkersArray);

    const baseUrl = 'URL REMOVED';

    for (let j = 0; j < polylineMarkersArray.length - 1; j++) {
      const origin = polylineMarkersArray[j].getPosition();
      const destination = polylineMarkersArray[j + 1].getPosition();
      
      const route = await createRoute(origin, destination);
      const encodedPolyline = route.overview_polyline;
      const queryUrl = baseUrl + '&polyline='+ encodedPolyline + '&distance=50';

      await fetch(queryUrl)
        .then((response) => response.json())
        .then( async (data) => await createChargerPointMarkers(data))
        .then (() => {
                   const k = j + 1;
          const l = polylineMarkersArray.length - 1;
          if (j === polylineMarkersArray.length - 2) {
            console.log('loop ' + k + ' of ' + l);
            console.log('EV markers creation finished');
          }else{
            console.log('loop ' + k + ' of ' + l);
          }
        });
    }
}

async createChargerPointMarkers(jsonChargingPoints): Promise<void> {
    // Convert the Json response elements to Google Markers, places them on the Map and pushes them to an array.
    for (const item of jsonChargingPoints) {
      const LatLng = new google.maps.LatLng(parseFloat(item.AddressInfo.Latitude), parseFloat(item.AddressInfo.Longitude));
      const marker = await new google.maps.Marker({
        position: LatLng,
        map: this.map,
        draggable: false,
      });
      this.markerArray.push(marker);
      this.chargingPointsMarkers.push(marker);
    }
  }

  async createRoute(point1, point2): Promise<google.maps.DirectionsRoute> {
    // Returns a Google DirectionsRoute object
    const directionsService = new google.maps.DirectionsService();
    const request = {
      origin: point1,
      destination: point2,
      travelMode: google.maps.TravelMode.DRIVING,
      unitSystem: google.maps.UnitSystem.METRIC
    };
    return new Promise(resolve => directionsService.route(request,
      (result, status) => {
        if (status === 'OK') {
          resolve(result.routes[0]);
        } else {
          window.alert('Directions request failed due to ' + status);
        }
      })
    );
  }

Answer №1

Upon review, it is clear that your code functions correctly as intended. The issue arises from the inclusion of alert(), which, when triggered, halts any further code execution in your browser and hinders UI rendering.

This behavior can be replicated with various code snippets that interact with the DOM.

const el = document.createElement("div");
const text = document.createTextNode("Hello world");

el.appendChild(text);
document.body.appendChild(el);

console.log('done');
alert('done');

The alert pops up after the node is appended to the DOM but before the browser finishes rendering it (at least in my testing environment).

If you replace the alert() with a console.log() in your code and add another console.log('marker added') for each google.maps.Marker() creation, you will observe the events unfolding in the expected sequence:

  1. (84) marker added
  2. loop 6 of 6
  3. EV markers creation finished
  4. calculateAndDisplayRoute method called

However, the alert() triggers before the browser completes rendering the markers.

To prevent confusion, it's advisable to refrain from relying on alert() for debugging or to use it cautiously due to its potentially misleading nature.

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

An array containing concatenated values should be transferred to the children of the corresponding value

Consider this example with an array: "items": [ { "value": "10", "label": "LIMEIRA", "children": [] }, { "value": "10-3", "label": "RECEBIMENTO", ...

Incorporating and modifying a component's aesthetics using styled-components: A comprehensive guide

My OverviewItem component has 2 props and is a styled-component. I want to change just one style on this component, which can be done by using the technique of styling any component. Does creating a wrapper component remain the only option for sharing st ...

Learn the process of showcasing database content on a webpage with an interactive feature that enables users to choose and access additional details

Apologies if this question has been asked before, I have searched for a solution but my web development knowledge is limited. To better understand my issue, you can visit the site at 000freewebhost by following this link: In summary, I am trying to select ...

The Chrome extension is unable to add text to the existing window

Lately, I've been attempting to develop an extension that will automatically add a div to the beginning of the current page. I've been following the guide provided on this https://developer.chrome.com/extensions/activeTab page. The code from the ...

Enable the feature for users to upload images to a specific folder within the Chrome extension without the need for

I need to implement a feature in my Chrome extension that allows users to upload images directly to a specific folder named "upload" without needing a submit button. <form action="/upload"> <input type="file" name="myimages" accept="image/*"> ...

Toggle class on child element when parent is clicked

I am currently working on a functional React component that looks like this: const RefreshButton = () => ( <IconButton> <RefreshIcon /> </IconButton> ) My goal is to dynamically assign a class attribute ...

express-validator: bypass additional validation in a user-defined validator

Utilizing the express-validator package for validating my request data. As per the documentation, we need to declare them in this manner: app.use(expressValidator({ customValidators: { isArray: function(value) { return Array.isArray(value); ...

Tips for initiating a function at a designated scroll point on a webpage

Currently, I am testing out a code snippet that allows images to flip through in a canvas element. I'm curious if it's possible to delay the image flipping effect until the viewer scrolls down to a specific section of the page where the canvas i ...

What is the best method to determine if a Django object is null in JavaScript?

Is there a way to verify if a Django object is null in JavaScript? At this moment, I have a variable called person which can hold a Django object or null. In my JavaScript code, I am using: if ({{person}} != null) { execute_function({{person}}) } Ca ...

Can I combine tuple types in Typescript?

type A1 = ['x','y','z'] type A2 = ['u','v','w'] type AN = [.., .., ..] type C = Combine<A1,A2,...,AN> //or Combine<[A1,A2,...,AN]> //resulting in ['x','y','z& ...

Framer Motion: Embrace the End of Animation Looping

Seeking help in creating a looped animation using the Framer Motion library with a slight delay. The animation plays upon mounting and webpage refresh but does not repeat as desired. Have reviewed the documentation but struggling with the syntax. const An ...

Using Node.js, we can create a program that makes repetitive calls to the same API in a

My task involves making recursive API calls using request promise. After receiving the results from the API, I need to write them into an excel file. Below is a sample response from the API: { "totalRecords": 9524, "size": 20, "currentPage": 1, "totalPage ...

Sending Angular base64 image data to the server

I am encountering an issue while attempting to upload a base64 image from Angular to ExpressJS. The image is being created using html2canvas to generate the base64 representation. When I try to upload the imageData in its current format, I receive an error ...

Adding and removing dynamic fields with Bootstrap functionality

Recently, I've been trying to develop a feature where users can add and remove fields by clicking on a button. However, I've encountered a roadblock in my progress. If you take a look at this CodePen link, you'll see what I have so far. My a ...

The choices in the cell table selection are obscured due to the select table's height when opened

I am experiencing an issue with a table where each row contains two cells with "select" options. The problem arises when I open them, as they load within the table and the available options are not clearly visible. I can only view all the options by scroll ...

Harness the Power of Generics in TypeScript for Automatic Type Inference

function execute<T>(operation1: (input: T) => void, operation2: (input: { key: string }) => T): void {} execute((params) => {}, () => 23); // The params here can be correctly inferred as number execute((params) => {}, (arg) => 23) ...

Exploring the World of Observables within AngularJS

I've been experimenting with some exercises to grasp the inner workings of AngularJS, but I'm feeling a little lost at the moment. Despite scouring through the API, various documentation, and tutorials, I haven't quite found what I'm l ...

Attempting to iterate over a JSON object and display its contents using HTML

I have been attempting to iterate through this JSON data and display it in an accordion format similar to the one shown here: but unfortunately, my implementation is not functioning correctly. Here is what I currently have: HTML: <div class="a ...

Steps for sending Ajax data to your server in order to make changes to your SharePoint list information

After spending a considerable amount of time working on my DataTable, I've managed to incorporate all the necessary functionalities except for one. Currently, my table retrieves data from a SharePoint list through an AJAX "GET" Request and organizes i ...

__dirname value not able to be retrieved

Currently, I'm utilizing __dirname to obtain the absolute path to the GraphQL schema: const schema = loadSchemaSync(path.join(__dirname, './graphql/schemas/schema.graphql'), { loaders: [new GraphQLFileLoader()] }); After transitioning the ...