186 lines
6.0 KiB
JavaScript
186 lines
6.0 KiB
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;
|