简体   繁体   中英

Why is my Google Maps API callback executing before the preceding inline script?

<script src="/js/site.js?v=EtZhOXlkRte5HDRXpfS7rDy3ua0sbCvtNJUaIlNsSXw"></script>
<script>
    var init;
    $(function () {
        var adpSummaryListenerId;
        init = _initializeDirections;
        initializeAdpSummaryListener();

        function _initializeDirections() {
            console.log("test"); //we don't get here, sometimes
            var origin = {
                isGoogle: $("#origin-isgoogle").val(),
                coordinate: new google.maps.LatLng(@origin.Latitude, @origin.Longitude),
                address: "@origin.StreetAddress",
                marker: "@origin.MarkerName"
            }, destination = {
                isGoogle: $("#destination-isgoogle").val(),
                coordinate: new google.maps.LatLng(@destination.Latitude, @destination.Longitude),
                address: "@destination.StreetAddress",
                marker: "@destination.MarkerName"
            }, start = {
                value: origin.isGoogle ? origin.address : origin.coordinate,
                pos: origin.coordinate,
                marker: origin.marker
            }, end = {
                value: destination.isGoogle ? destination.address : destination.coordinate,
                pos: destination.coordinate,
                marker: destination.marker
            };

            console.log("Initializing directions");
            initializeDirections(start, end); //in site.js
        }

        function initializeAdpSummaryListener() {
            adpSummaryListenerId = window.setInterval(addNavigateButton, 500);
        }

        function addNavigateButton() {
            console.log("checking for .adp-summary");
            if ($(".adp-summary").length) {
                $(".adp-summary").append($("#start-navigation").html());
                clearInterval(adpSummaryListenerId);
            }
        }
    });
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=@(Model.GoogleMapsApiKey)&callback=init"></script>

I can't figure out why, despite the maps script being referenced after the inline script, the Google Maps API callback init is sometimes not instantiated before Google attempts to call it.

This is the error I'm getting:

Uncaught (in promise)
ld {message: "init is not a function", name: "InvalidValueError", stack: "Error at new ld (https://maps.googleapis.com/.......&callback=init:141:124"}

I previously had the callback name as initializeMap as a reference declared in site.js (but still initialized here in the inline script), but moved it here for brevity and isolation, hoping I would be able to figure out the issue. But the same problem persists.

I understand that async directs the api to be fetched in parallel to parsing and evaluated as soon as it is available, but it shouldn't be available before the inline script, right?

I also understand that defer indicates that the api won't be executed until after the document is parsed, so it still shouldn't be available yet.

I did see this about defer :

This attribute allows the elimination of parser-blocking JavaScript where the browser would have to load and evaluate scripts before continuing to parse. async has a similar effect in this case.

But defer still shouldn't prevent the inline script from executing first because it only enables JavaScript to not have to be parser-blocking if the attribute is used on the inline <script> tag.

So I'm at a loss.

Your above inline script is indeed running first, but the $(function () { is a jQuery construct which only runs the callback inside once the document is ready. It looks like the Google script is running first.

If you give Google's script the defer attribute, it will only run once the DOM is finished being built, and the $(function () { will be unnecessary. So, just move everything out of the $(function () { so that init will be on the top level, and callable by Google when Google's script loads:

<script>
function init() {
            var origin = {
                isGoogle: $("#origin-isgoogle").val(),
                coordinate: new google.maps.LatLng(@origin.Latitude, @origin.Longitude),
                address: "@origin.StreetAddress",
                marker: "@origin.MarkerName"
            }, destination = 
            // etc

Note that while you can use async or defer , you shouldn't use both. To wait until the DOM is loaded, use defer and not async .

There is one thing that your above inline script does other than assign to variables, which is call initializeAdpSummaryListener . You can put this inside $(function () { if you need to make sure the DOM is loaded before it runs:

$(initializeAdpSummaryListener);

Another option whose control flow might be easier to understand would be to put everything in a named function that Google calls when it loads (which, given the defer attribute, will occur only once the page loads).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM