UNPKG

futoin-asyncsteps

Version:

Mimic traditional threads in single threaded event loop

374 lines (351 loc) 10.5 kB
"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