time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

async.js (4957B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 'use strict';
     20 
     21 // MODULES //
     22 
     23 var logger = require( 'debug' );
     24 var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives;
     25 var isFunction = require( '@stdlib/assert/is-function' );
     26 var copy = require( '@stdlib/utils/copy' );
     27 var readFile = require( './../../read-file' );
     28 var config = require( './config.json' );
     29 var delay = require( './delay.js' );
     30 var clearPending = require( './clear_pending.js' );
     31 
     32 
     33 // VARIABLES //
     34 
     35 var debug = logger( 'read-file-list:async' );
     36 var MAX_RETRIES = config.max_retries;
     37 var MAX_DELAY = config.max_delay;
     38 
     39 
     40 // MAIN //
     41 
     42 /**
     43 * Asynchronously reads the entire contents of each file in a file list.
     44 *
     45 * @param {StringArray} list - list of file paths
     46 * @param {(Object|string)} [options] - options
     47 * @param {(string|null)} [options.encoding] - file encoding
     48 * @param {string} [options.flag] - file status flag
     49 * @param {Function} clbk - callback to invoke after reading file contents
     50 * @throws {TypeError} first argument must be a string array
     51 * @throws {TypeError} callback argument must be a function
     52 *
     53 * @example
     54 * var list = [ __filename ];
     55 *
     56 * readFileList( list, onFiles );
     57 *
     58 * function onFiles( error, files ) {
     59 *     if ( error ) {
     60 *         throw error;
     61 *     }
     62 *     console.dir( files );
     63 * }
     64 */
     65 function readFileList( list, options, clbk ) {
     66 	var pending;
     67 	var results;
     68 	var errFLG;
     69 	var count;
     70 	var opts;
     71 	var len;
     72 	var cb;
     73 	var i;
     74 
     75 	if ( !isStringArray( list ) ) {
     76 		throw new TypeError( 'invalid argument. First argument must be a string array. Value: `' + list + '`.' );
     77 	}
     78 	if ( arguments.length < 3 ) {
     79 		opts = {};
     80 		cb = options;
     81 	} else {
     82 		opts = copy( options );
     83 		cb = clbk;
     84 	}
     85 	if ( !isFunction( cb ) ) {
     86 		throw new TypeError( 'invalid argument. Callback argument must be a function. Value: `' + cb + '`.' );
     87 	}
     88 	len = list.length;
     89 
     90 	results = new Array( len );
     91 	pending = {};
     92 	count = 0;
     93 
     94 	debug( 'Reading %d files...', len );
     95 	for ( i = 0; i < len; i++ ) {
     96 		debug( 'Reading file: %s (%d of %d).', list[ i ], i+1, len );
     97 		readFile( list[ i ], opts, getCallback( i ) );
     98 	}
     99 
    100 	/**
    101 	* Returns a callback to be invoked upon reading a file.
    102 	*
    103 	* @private
    104 	* @param {NonNegativeInteger} idx - index
    105 	* @returns {Callback} callback
    106 	*/
    107 	function getCallback( idx ) {
    108 		var retries;
    109 		var file;
    110 		var k;
    111 
    112 		file = list[ idx ];
    113 		k = idx + 1;
    114 		retries = 0;
    115 
    116 		/**
    117 		* Retries reading a file.
    118 		*
    119 		* @private
    120 		*/
    121 		function retry() {
    122 			delete pending[ idx ];
    123 			debug( 'Reading file: %s (%d of %d). Retry: %d of %d.', file, k, len, retries, MAX_RETRIES );
    124 			readFile( file, opts, onRead );
    125 		}
    126 
    127 		/**
    128 		* Callback to be invoked upon reading a file.
    129 		*
    130 		* @private
    131 		* @param {(Error|null)} error - error object
    132 		* @param {(Buffer|string)} data - file data
    133 		* @returns {void}
    134 		*/
    135 		function onRead( error, data ) {
    136 			var d;
    137 			if ( errFLG ) {
    138 				debug( 'An error has already been returned. Discarding data for file: %s (%d of %d).', file, k, len );
    139 				return; // prevents `done()` from being called more than once
    140 			}
    141 			if ( error ) {
    142 				debug( 'Encountered an error when reading %s (%d of %d). Error: %s', file, k, len, error.message );
    143 				if (
    144 					error.code === 'EMFILE' || // current process
    145 					error.code === 'ENFILE'    // across entire system
    146 				) {
    147 					retries += 1;
    148 					if ( retries > MAX_RETRIES ) {
    149 						debug( 'Maximum number of retries exceeded. Too many open files.' );
    150 						error = new Error( 'max retries exceeded. Too many open files.' );
    151 						return done( error );
    152 					}
    153 					d = delay( retries, MAX_DELAY );
    154 					debug( 'Too many open files. Will retry reading file %d of %d in %s seconds.', k, len, d/1000 );
    155 					pending[ idx ] = setTimeout( retry, d );
    156 					return;
    157 				}
    158 				return done( error );
    159 			}
    160 			debug( 'Successfully read file: %s (%d of %d).', file, k, len );
    161 			results[ idx ] = {
    162 				'file': file,
    163 				'data': data
    164 			};
    165 			count += 1;
    166 			debug( 'Read %d of %d files.', count, len );
    167 			if ( count === len ) {
    168 				return done();
    169 			}
    170 		}
    171 
    172 		return onRead;
    173 	}
    174 
    175 	/**
    176 	* Callback invoked upon completion.
    177 	*
    178 	* @private
    179 	* @param {Error} [error] - error object
    180 	* @returns {void}
    181 	*/
    182 	function done( error ) {
    183 		clearPending( pending );
    184 		if ( error ) {
    185 			errFLG = true;
    186 			return cb( error );
    187 		}
    188 		debug( 'Successfully read all files.' );
    189 		cb( null, results );
    190 	}
    191 }
    192 
    193 
    194 // EXPORTS //
    195 
    196 module.exports = readFileList;