UNPKG

futoin-asyncsteps

Version:

Mimic traditional threads in single threaded event loop

176 lines (151 loc) 3.99 kB
"use strict"; /** * @file Implementation of parallel step * @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. */ const { checkFunc, checkOnError, } = require( './common' ); /* globals AsyncSteps */ /** * ParallelStep * @private * @class * @param {AsyncSteps} [root] reference to current root object * @param {AsyncSteps} [as] reference to current step object */ class ParallelStep { constructor( root, as ) { this._root = root; this._as = as; this._queue = []; this._psteps = []; this._complete_count = 0; } /** * @private * @override */ add( func, onerror ) { checkFunc( this, func ); checkOnError( this, onerror ); this._queue.push( [ func, onerror ] ); return this; } /** * @private */ _complete( ) { this._complete_count += 1; if ( this._complete_count === this._psteps.length ) { this._as._root._handle_success(); this._cleanup(); } } /** * @private * @param {string} [name] Error name * @param {string} [info] Error info */ _error( name, info ) { try { this._as.error( name, info ); } catch { // ignore } } /** * @private * @param {AsyncSteps} [as] current step interface */ executeParallel( as ) { const q = this._queue; const root = this._root; if ( root !== as._root ) { const p = new ParallelStep( as._root, as ); p._queue.push.apply( p._queue, q ); p.executeParallel( as ); return; } this._as = as; if ( !q.length ) { this._complete(); return; } as._on_cancel = () => { this.cancel(); }; /* */ const plist = this._psteps; const success_func = ( as ) => { this._complete(); }; const error_func = ( as, err ) => { this._error( err, as.state.error_info ); }; const AsyncSteps = root.constructor; q.forEach( ( p ) => { const pa = new AsyncSteps( as.state, root._async_tool ); pa._queue.push( [ ( as ) => { as._queue = [ [ p[0], p[1] ], ]; }, error_func, ], [ success_func, undefined ], ); plist.push( pa ); } ); // Should be separate from the previous loop for // in case cancel() arrives in the middle plist.forEach( ( p ) => { // NOTE: do not use #_execute() due to lack of burst ownership handling p.execute(); } ); } /** * @private */ cancel() { this._psteps.forEach( ( p ) => { p.cancel(); } ); this._cleanup(); } /** * @private * @override */ isAsyncSteps() { return true; } /** * @private */ _cleanup() { this._root = null; this._as = null; this._psteps = null; } } module.exports = ParallelStep;