cordova-plugin-googlemaps
Version:
Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.
186 lines (161 loc) • 6.16 kB
JavaScript
/*****************************************************************************
* Command queue mechanism
* (Save the number of method executing at the same time)
*****************************************************************************/
var cordova_exec = require('cordova/exec'),
common = require('./Common');
var commandQueue = [];
var _isWaitMethod = null;
var _isExecuting = false;
var _executingCnt = 0;
var MAX_EXECUTE_CNT = 10;
var _lastGetMapExecuted = 0;
var _isResizeMapExecuting = false;
// This flag becomes true when the page will be unloaded.
var _stopRequested = false;
function execCmd(success, error, pluginName, methodName, args, execOptions) {
execOptions = execOptions || {};
// The JavaScript special keyword 'this' indicates `who call this function`.
// This execCmd function is executed from overlay instances such as marker.
// So `this` means `overlay` instance.
var overlay = this;
// If the overlay has been already removed from map,
// do not execute any methods on it.
if (overlay._isRemoved && !execOptions.remove) {
console.error('[ignore]' + pluginName + '.' + methodName + ', because removed.');
return true;
}
// If the overlay is not ready in native side,
// do not execute any methods except remove on it.
// This code works for map class especially.
if (!this._isReady && methodName !== 'remove') {
console.error('[ignore]' + pluginName + '.' + methodName + ', because it\'s not ready.');
return true;
}
// Push the method into the commandQueue(FIFO) at once.
commandQueue.push({
'execOptions': execOptions,
'args': [
function() {
//-------------------------------
// success callback
//-------------------------------
if (methodName === 'resizeMap') {
_isResizeMapExecuting = false;
}
// Even if the method was successful,
// but the _stopRequested flag is true,
// do not execute further code.
if (!_stopRequested && success) {
var results = Array.prototype.slice.call(arguments, 0);
common.nextTick(function() {
success.apply(overlay,results);
});
}
// Insert small delays only for `map.getMap()`,
// because if you execute three or four `map.getMap()` at the same time,
// Android OS itself crashes.
// In order to prevent this error, insert small delays.
var delay = 0;
if (methodName === _isWaitMethod) {
if (_isWaitMethod === 'getMap' && Date.now() - _lastGetMapExecuted < 1500) {
delay = 1500;
}
_lastGetMapExecuted = Date.now();
_isWaitMethod = null;
}
setTimeout(function() {
_executingCnt--;
common.nextTick(_exec);
}, delay);
},
function() {
//-------------------------------
// error callback
//-------------------------------
if (methodName === 'resizeMap') {
_isResizeMapExecuting = false;
}
if (!_stopRequested && error) {
var results = Array.prototype.slice.call(arguments, 0);
common.nextTick(function() {
error.apply(overlay,results);
});
}
if (methodName === _isWaitMethod) {
_isWaitMethod = null;
}
_executingCnt--;
common.nextTick(_exec);
},
pluginName, methodName, args]
});
// In order to execute all methods in safe,
// the maps plugin limits the number of execution in a moment to 10.
//
// Note that Cordova-Android drops has also another internal queue,
// and the internal queue drops our statement if the app send too much.
//
// Also executing too much statements at the same time,
// it would cause many errors in native side, such as out-of-memory.
//
// In order to prevent these troubles, the maps plugin limits the number of execution is 10.
if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) {
return;
}
common.nextTick(_exec);
}
function _exec() {
// You probably wonder why there is this code because it's already simular code at the end of the execCmd function.
//
// Because the commandQueue might change after the last code of the execCmd.
// (And yes, it was occurred.)
// In order to block surely, block the execution again.
if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) {
return;
}
_isExecuting = true;
// Execute some execution requests (up to 10) from the commandQueue.
var methodName;
while (commandQueue.length > 0 && _executingCnt < MAX_EXECUTE_CNT) {
if (!_stopRequested) {
_executingCnt++;
}
// Pick up the head one.
var commandParams = commandQueue.shift();
methodName = commandParams.args[3];
// If the request is `map.refreshLayout()` and another `map.refreshLayout()` is executing,
// skip it.
// This prevents to execute multiple `map.refreshLayout()` at the same time.
if (methodName === 'resizeMap') {
if (_isResizeMapExecuting) {
_executingCnt--;
continue;
}
_isResizeMapExecuting = true;
}
// If the `_stopRequested` flag is true,
// do not execute any statements except `remove()` or `clear()` methods.
if (_stopRequested && (!commandParams.execOptions.remove || methodName !== 'clear')) {
_executingCnt--;
continue;
}
// Some methods have to block other execution requests, such as `map.clear()`
if (commandParams.execOptions.sync) {
_isWaitMethod = methodName;
cordova_exec.apply(this, commandParams.args);
break;
}
cordova_exec.apply(this, commandParams.args);
}
_isExecuting = false;
}
//----------------------------------------------------
// Stop all executions if the page will be closed.
//----------------------------------------------------
function stopExecution() {
// Request stop all tasks.
_stopRequested = true;
}
window.addEventListener('unload', stopExecution);
module.exports = execCmd;