<commit_before>Utils for async
===============

	'use strict'

	[utils, expect] = ['./index.coffee.md', 'expect'].map require

	{exports} = module

	{shift} = Array::
	{isArray} = Array

	NOP = ->

*class* Stack
-------------

Store asynchronous functions in list.
Functions have to provide `callback` as last parameter.

	exports.Stack = class Stack

### Constructor

		constructor: ->

			@_arr = []

### Properties

#### Protected

		_arr: null

### Methods

#### add()

Add new asynchronous function into stack.

`func` is a name of function stored in `obj` or function to call;
`obj` is a namespace where specified `func` exists and context passed into function;
`args` are an array of arguments passed into function.

		add: (func, context, args) ->
			expect().defined(args).toBe.object()

			@_arr.push func, context, args

#### callNext()

Call next function from the stack.

`callback` is provided into function.

Empty `callback` will be called if there is no function to call.

		callNext: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync callNext(): passed callback is not a function"

			# on empty
			unless @_arr.length
				return callback null

			# get next
			func = @_arr.shift()
			context = @_arr.shift()
			args = @_arr.shift()

			if typeof func is 'string'
				func = context[func]

			if typeof func isnt 'function'
				throw new TypeError "ASync Stack::callNext(): function to call is not a function"

			# add callback into args
			# To avoid got args array modification and to minimise memory usage,
			# we create new object with `args` as a prototype.
			# `Function::apply` expects an object and iterate by it to the `length`.
			args = Object.create (args or null)
			args[func.length - 1] = callback
			if args.length is undefined or args.length < func.length
				args.length = func.length

			# call; support sync errors
			utils.tryFunc func, context, args, callback

#### runAll()

Run all stored functions in order.

`callback` has arguments from the last fulfilled or first rejected fuction.

		runAll: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync runAll(): passed callback is not a function"

			unless @_arr.length
				return callback null

			onNextCalled = (err) =>

				# on err
				if err
					return callback err

				# call next
				if @_arr.length
					return callNext()

				callback.apply null, arguments

			callNext = @callNext.bind @, onNextCalled

			callNext()

#### runAllSimultaneously()

Run all stored functions in the same time.

`callback` has arguments from the last fulfilled or first rejected fuction.

		runAllSimultaneously: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync runAllSimultaneously(): passed callback is not a function"

			length = n = @_arr.length / 3
			done = 0
<commit_msg>`async.Stack` callbacks now can't be called twice<commit_after>Utils for async
===============

	'use strict'

	[utils, expect] = ['./index.coffee.md', 'expect'].map require

	{exports} = module
	{assert} = console

	{shift} = Array::
	{isArray} = Array

	NOP = ->

*class* Stack
-------------

Store asynchronous functions in list.
Functions have to provide `callback` as last parameter.

	exports.Stack = class Stack

### Constructor

		constructor: ->

			@_arr = []

### Properties

#### Protected

		_arr: null

### Methods

#### add()

Add new asynchronous function into stack.

`func` is a name of function stored in `obj` or function to call;
`obj` is a namespace where specified `func` exists and context passed into function;
`args` are an array of arguments passed into function.

		add: (func, context, args) ->
			expect().defined(args).toBe.object()

			@_arr.push func, context, args

#### callNext()

Call next function from the stack.

`callback` is provided into function.

Empty `callback` will be called if there is no function to call.

		callNext: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync callNext(): passed callback is not a function"

			# on empty
			unless @_arr.length
				return callback null

			# get next
			func = @_arr.shift()
			context = @_arr.shift()
			args = @_arr.shift()

			if typeof func is 'string'
				func = context[func]

			if typeof func isnt 'function'
				throw new TypeError "ASync Stack::callNext(): function to call is not a function"

			syncError = null
			called = false

			callbackWrapper = ->
				assert not called or not syncError
				, "Callback can't be called if function throws an error;\n" +
				  "Function: `#{func}`\nSynchronous error: `#{syncError}`"

				assert not called
				, "Callback can't be called twice;\nFunction: `#{func}`"

				called = true
				callback.apply @, arguments

			# add callback into args
			# To avoid got args array modification and to minimise memory usage,
			# we create new object with `args` as a prototype.
			# `Function::apply` expects an object and iterate by it to the `length`.
			args = Object.create (args or null)
			args[func.length - 1] = callbackWrapper
			if args.length is undefined or args.length < func.length
				args.length = func.length

			# call; support sync errors
			syncError = utils.catchError func, context, args
			if syncError
				callbackWrapper syncError

#### runAll()

Run all stored functions in order.

`callback` has arguments from the last fulfilled or first rejected fuction.

		runAll: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync runAll(): passed callback is not a function"

			unless @_arr.length
				return callback null

			onNextCalled = (err) =>

				# on err
				if err
					return callback err

				# call next
				if @_arr.length
					return callNext()

				callback.apply null, arguments

			callNext = => @callNext onNextCalled

			callNext()

#### runAllSimultaneously()

Run all stored functions in the same time.

`callback` has arguments from the last fulfilled or first rejected fuction.

		runAllSimultaneously: (callback) ->

			if typeof callback isnt 'function'
				throw new TypeError "ASync runAllSimultaneously(): passed callback is not a function"

			length = n = @_arr.length / 3
			done = 0

{'fraction_non_alphanumeric': 0.10422036082474227, 'fraction_numerical': 0.0009664948453608248, 'mean_word_length': 3.249828884325804, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2829512', 'n_tokens_mistral': 2099, 'n_tokens_neox': 1760, 'n_words': 735}