简体   繁体   English

是否可以在不创建全局回调函数的情况下异步初始化Google地图?

[英]Is it possible to initialize Google Maps asynchronously without creating a global callback function?

I am trying to write a re-usable drop-in Module to load Google Maps asynchronously and return a promise. 我正在尝试编写一个可重复使用的插件模块,以异步方式加载Google Maps并返回一个承诺。

Here is the code I came up with, using AngularJS . 这是我提出的代码,使用AngularJS

However, there is a re-usability drawback of creating a global callback function "behind the scene". 但是,在“幕后”创建全局回调函数存在可重用性的缺点。 Which is a side effect that may result in bugs, if the same namespace happens to be used by any other library. 如果任何其他库碰巧使用相同的命名空间,则可能导致错误的副作用。

My questions - is there any way to achieve this effect without creating global variables. 我的问题 - 有没有办法在不创建全局变量的情况下实现这种效果。

Below is the code that creates that "evil" global callback: 下面是创建“邪恶”全局回调的代码:

// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
    .factory('Initializer', function($window, $q){

        // maps loader deferred object
        var mapsDefer = $q.defer();

        // Google's url for async maps initialization accepting callback function
        var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=';

        // async loader
        var asyncLoad = function(asyncUrl, callbackName) {
          var script = document.createElement('script');
          //script.type = 'text/javascript';
          script.src = asyncUrl + callbackName;
          document.body.appendChild(script);
        };

// Here is the bad guy:

        // callback function - resolving promise after maps successfully loaded
        $window.googleMapsInitialized = function () {
            mapsDefer.resolve();
        };

        // loading google maps
        asyncLoad(asyncUrl, 'googleMapsInitialized');

        return {

            // usage: Initializer.mapsInitialized.then(callback)
            mapsInitialized : mapsDefer.promise
        };
    })

The script loading technique for getting data from a server on a cross-origin domain that you are referring to is JSONP. 用于从您引用的跨源域上的服务器获取数据的脚本加载技术是JSONP。 You can read more about it here . 你可以在这里阅读更多相关信息。 Somewhat by definition, JSONP can only work by calling a globally scoped function. 根据定义,JSONP只能通过调用全局范围的函数来工作。

So, in direct answer to your question: no you can't use JSONP cross-origin techniques without the global function - that's just how the mechanism works. 所以,直接回答你的问题:没有全局函数,你不能使用JSONP跨源技术 - 这就是机制的工作原理。 A script is executed in the global namespace and it has to call a function it can reach from the global namespace. 脚本在全局命名空间中执行,它必须调用它可以从全局命名空间访问的函数。 Even jQuery and YUI do something like this for implementing JSONP. 甚至jQuery和YUI都是这样做的,用于实现JSONP。

And, since you are using Angular, it already has JSONP functionality built into it. 而且,由于您使用的是Angular,因此它已经内置了JSONP功能。 See the doc here so you don't have to create your own mechanism for doing this. 请参阅此处的doc,这样您就不必创建自己的机制来执行此操作。


But, that said, if you were making your own, you can make it much less likely that your global function will collide with anyone else's code or even with another instance of your library by taking some precautions to make the global names you create more random. 但是,这就是说,如果你自己制作,你可以通过采取一些预防措施使你创建的全局名称更随机,使你的全局函数与其他人的代码或甚至与你的库的另一个实例发生碰撞的可能性大大降低。


Here's an example of how you can make the odds of any sort of naming collision very small. 这是一个如何使任何类型的命名冲突的几率非常小的例子。 This uses three techniques: 这使用三种技术:

  1. Use some leading underscores on your prefix. 在前缀上使用一些前导下划线。
  2. Add a random sequence of digits to the function name. 在函数名称中添加一个随机数字序列。
  3. Add a time stamp to the function name. 在函数名称中添加时间戳。
  4. Remove the global after it is used. 使用后删除全局。

Here is your code with those aspects implemented. 这是您实现这些方面的代码。

// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
    .factory('Initializer', function($window, $q){

        // maps loader deferred object
        var mapsDefer = $q.defer();

        // Google's url for async maps initialization accepting callback function
        var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=';

        // async loader
        var asyncLoad = function(asyncUrl, callbackName) {
          var script = document.createElement('script');
          //script.type = 'text/javascript';
          script.src = asyncUrl + callbackName;
          document.body.appendChild(script);
        };

        // generate a unique function name
        // includes prefix, current time and random number
        var fname = "__googleMapsInitialized_" + 
            (new Date().getTime()) + "_" + 
            (Math.random() + "").replace(".", "");

        // callback function - resolving promise after maps successfully loaded
        $window[fname] = function () {
            mapsDefer.resolve();
            // remove the global now that we're done with it
            delete $window[fname];
        };
        // loading google maps
        asyncLoad(asyncUrl, fname);

        return {

            // usage: Initializer.mapsInitialized.then(callback)
            mapsInitialized : mapsDefer.promise
        };
    })

Demo of the unique function name generator: http://jsfiddle.net/jfriend00/oms7vc6o/ 演示独特的函数名称生成器: http//jsfiddle.net/jfriend00/oms7vc6o/

PS I don't know Angular myself, but it appears that Angular already knows how to make JSONP calls all by itself so you don't have to make your own solution here. PS我自己并不认识Angular,但似乎Angular已经知道如何单独进行JSONP调用,因此您不必在此处制作自己的解决方案。 See this Angular doc page and this other question and this article for details. 有关详细信息,请参阅此Angular文档页面以及此其他问题本文

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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