I am trying to write a re-usable drop-in Module to load Google Maps
asynchronously and return a promise.
Here is the code I came up with, using 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. You can read more about it here . Somewhat by definition, JSONP can only work by calling a globally scoped function.
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. 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.
And, since you are using Angular, it already has JSONP functionality built into it. See the doc here so you don't have to create your own mechanism for doing this.
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:
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/
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. See this Angular doc page and this other question and this article for details.
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.