futoin-asyncsteps
Version:
Mimic traditional threads in single threaded event loop
374 lines (351 loc) • 10.5 kB
JavaScript
"use strict";
/**
* @file Protector against AsyncSteps concept violation in use
* @author Andrey Galkin <andrey@futoin.org>
*
*
* Copyright 2014-2017 FutoIn Project (https://futoin.org)
* Copyright 2014-2017 Andrey Galkin <andrey@futoin.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
var ParallelStep = require('./ParallelStep');
var _require = require('../Errors'),
InternalError = _require.InternalError,
Timeout = _require.Timeout,
LoopBreak = _require.LoopBreak,
LoopCont = _require.LoopCont;
var _require2 = require('./common'),
isProduction = _require2.isProduction,
checkFunc = _require2.checkFunc,
checkOnError = _require2.checkOnError,
noop = _require2.noop,
_loop = _require2.loop,
_repeat = _require2.repeat,
_forEach = _require2.forEach,
LOOP_TERM_LABEL = _require2.LOOP_TERM_LABEL,
as_await = _require2.as_await;
var sanityCheck = isProduction ? noop : function (asp) {
var root = asp._root;
if (root) {
var stack = root._stack;
if (stack) {
if (stack[stack.length - 1] === asp) {
return;
}
root.error(InternalError, "Invalid call (sanity check)");
}
}
throw new Error("InternalError: Unexpected call, object is out of service");
};
var sanityCheckAdd = isProduction ? noop : function (asp, func, onerror) {
sanityCheck(asp);
checkFunc(asp, func);
checkOnError(asp, onerror);
};
var on_timeout = function on_timeout(asi) {
asi._limit_event = null;
var state = asi.state;
state.error_info = undefined;
state.last_exception = new Error(Timeout);
asi._root._handle_error(Timeout);
};
/* globals AsyncSteps */
/* globals CancelFunc */
/**
* AsyncStepProtector
* @private
* @class
* @param {AsyncSteps} [root] main object
*/
var AsyncStepProtector = /*#__PURE__*/function () {
function AsyncStepProtector(root, on_error, call_args) {
_classCallCheck(this, AsyncStepProtector);
this._root = root;
this.state = root.state;
this._queue = null;
this._call_args = call_args;
this._on_error = on_error;
this._on_cancel = null;
this._limit_event = null;
}
/**
* @private
* @override
*/
return _createClass(AsyncStepProtector, [{
key: "add",
value: function add(func, onerror) {
sanityCheckAdd(this, func, onerror);
var s = [func, onerror];
var q = this._queue;
if (q) {
q.push(s);
} else {
this._queue = [s];
}
return this;
}
/**
* @private
* @override
*/
}, {
key: "parallel",
value: function parallel(onerror) {
var p = new ParallelStep(this._root, this);
this.add(function (as) {
p.executeParallel(as);
}, onerror);
return p;
}
/**
* Successfully complete current step execution, optionally passing result variables to the next step.
* @param {...any} [args] - unlimited number of result variables with no type constraint
* @alias AsyncSteps#success
*/
}, {
key: "success",
value: function success() {
sanityCheck(this);
if (this._queue !== null) {
this.error(InternalError, "Invalid success() call");
}
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
this._root._handle_success(args);
}
/**
* @private
* @override
*/
}, {
key: "error",
value: function error(name, error_info) {
sanityCheck(this);
this._root.error(name, error_info);
}
/**
* Set timeout for external event completion with async *as.success()* or *as.error()* call.
* If step is not finished until timeout is reached then Timeout error is raised.
* Can be used only within **ExecFunc** body.
* @param {number} timeout_ms - Timeout in ms
* @returns {AsyncSteps} self
* @alias AsyncSteps#setTimeout
*/
}, {
key: "setTimeout",
value: function setTimeout(timeout_ms) {
var _this = this;
sanityCheck(this);
var async_tool = this._root._async_tool;
if (this._limit_event !== null) {
async_tool.cancelCall(this._limit_event);
}
this._limit_event = async_tool.callLater(function () {
on_timeout(_this);
}, timeout_ms);
return this;
}
/**
* Set cancellation handler to properly handle timeouts and external cancellation.
* Can be used only within **ExecFunc** body.
* @param {CancelFunc} oncancel - cleanup/cancel logic of external processing
* @returns {AsyncSteps} self
* @alias AsyncSteps#setCancel
*/
}, {
key: "setCancel",
value: function setCancel(oncancel) {
this._on_cancel = oncancel;
return this;
}
/**
* Mark currently executing step as waiting for external event.
* Can be used only within **ExecFunc** body.
* @returns {AsyncSteps} self
* @alias AsyncSteps#waitExternal
*/
}, {
key: "waitExternal",
value: function waitExternal() {
this._on_cancel = noop;
return this;
}
/**
* @private
* @override
*/
}, {
key: "copyFrom",
value: function copyFrom(other) {
sanityCheck(this);
if (other._queue.length) {
var q = this._queue;
if (q === null) {
q = [];
this._queue = q;
}
q.push.apply(q, other._queue);
}
var os = other.state;
var s = this.state;
for (var k in os) {
if (s[k] === undefined) {
s[k] = os[k];
}
}
return this;
}
/**
* @private
* @override
*/
}, {
key: "loop",
value: function loop(func, label) {
sanityCheckAdd(this, func);
_loop(this, this._root, func, label);
return this;
}
/**
* @private
* @override
*/
}, {
key: "repeat",
value: function repeat(count, func, label) {
sanityCheckAdd(this, func);
_repeat(this, this._root, count, func, label);
return this;
}
/**
* @private
* @override
*/
}, {
key: "forEach",
value: function forEach(map_or_list, func, label) {
sanityCheckAdd(this, func);
_forEach(this, this._root, map_or_list, func, label);
return this;
}
/**
* Break execution of current loop, throws exception
* @param {string=} label - Optional. unwind loops, until *label* named loop is exited
* @alias AsyncSteps#break
*/
}, {
key: "break",
value: function _break(label) {
sanityCheck(this);
this.state[LOOP_TERM_LABEL] = label;
this._root.error(LoopBreak);
}
/**
* Continue loop execution from the next iteration, throws exception
* @param {string=} label - Optional. unwind loops, until *label* named loop is found
* @alias AsyncSteps#continue
*/
}, {
key: "continue",
value: function _continue(label) {
sanityCheck(this);
this.state[LOOP_TERM_LABEL] = label;
this._root.error(LoopCont);
}
/**
* @private
* @override
*/
}, {
key: "successStep",
value: function successStep() {
var _this2 = this;
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
sanityCheck(this);
var queue = this._queue;
if (queue && queue.length) {
queue.push([function () {
_this2._root._handle_success(args);
}, undefined]);
} else {
this._root._next_args = args;
}
return this;
}
/**
* @private
* @override
*/
}, {
key: "await",
value: function _await(promise, onerror) {
sanityCheck(this);
as_await(this, this._root, promise, onerror);
return this;
}
/**
* @private
* @override
*/
}, {
key: "sync",
value: function sync(object, func, onerror) {
sanityCheckAdd(this, func, onerror);
object.sync(this, func, onerror);
return this;
}
/**
* @private
* @override
*/
}, {
key: "newInstance",
value: function newInstance() {
return this._root.newInstance();
}
/**
* @private
* @override
*/
}, {
key: "isAsyncSteps",
value: function isAsyncSteps() {
return true;
}
/**
* @private
*/
}, {
key: "_cleanup",
value: function _cleanup() {
this._root = null;
//this._queue = null;
//this._on_error = null;
//this._on_cancel = null;
this.state = null;
}
}]);
}();
module.exports = AsyncStepProtector;
//# sourceMappingURL=AsyncStepProtector.js.map