Need some help with commandStack.execute

In the source code of commandStack, I found that when executing the execute(command, context) method, _poshAction(action) is executed to store the action object into _currentExecution.

Then, _internalExecute(action) is executed. But in the _internalExecute method, _poshAction(action) and _popAction() are still executed once, and the effect of the two _poshAction(action) looks the same, so why is it necessary to handle it this way?

CommandStack.prototype.execute = function(command, context) {
  if (!command) {
    throw new Error('command required');
  }

  this._currentExecution.trigger = 'execute';

  const action = { command: command, context: context };

  this._pushAction(action);
  this._internalExecute(action);
  this._popAction();
};
CommandStack.prototype._pushAction = function(action) {

  const execution = this._currentExecution,
        actions = execution.actions;

  const baseAction = actions[0];

  if (execution.atomic) {
    throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
  }

  if (!action.id) {
    action.id = (baseAction && baseAction.id) || this._createId();
  }

  actions.push(action);
};
CommandStack.prototype._internalExecute = function(action, redo) {
  const command = action.command,
        context = action.context;

  const handler = this._getHandler(command);

  if (!handler) {
    throw new Error('no command handler registered for <' + command + '>');
  }

  this._pushAction(action);

  if (!redo) {
    this._fire(command, 'preExecute', action);

    if (handler.preExecute) {
      handler.preExecute(context);
    }

    this._fire(command, 'preExecuted', action);
  }

  // guard against illegal nested command stack invocations
  this._atomicDo(() => {

    this._fire(command, 'execute', action);

    if (handler.execute) {

      // actual execute + mark return results as dirty
      this._markDirty(handler.execute(context));
    }

    // log to stack
    this._executedAction(action, redo);

    this._fire(command, 'executed', action);
  });

  if (!redo) {
    this._fire(command, 'postExecute', action);

    if (handler.postExecute) {
      handler.postExecute(context);
    }

    this._fire(command, 'postExecuted', action);
  }

  this._popAction();
};
CommandStack.prototype._popAction = function() {
  const execution = this._currentExecution,
        trigger = execution.trigger,
        actions = execution.actions,
        dirty = execution.dirty;

  actions.pop();

  if (!actions.length) {
    this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });

    dirty.length = 0;

    this._fire('changed', { trigger: trigger });

    execution.trigger = null;
  }
};

Hi @miyuesc. I’ll try to share some details, but please be mindful that these are internals, and can change / break any time!

What the command does is putting a clamp around a set of modeling actions, that are initially scaffolded (during pre-execute), then executed and later reverted. After execution it triggers some side-effects, cf. CommandStack#_popAction:

  if (!actions.length) {
    this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });

    ...
  }

If we at some point allow execution of more than one command, something that we already discussed internally, and what we work around in the properties panel, then we need the parent to ensure the transaction start and end are properly marked.

@nikku Thank you very much for your answer, I think I understand it。

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.