UNPKG

neft

Version:

Universal Platform

327 lines (237 loc) 8.79 kB
# Asynchronous Access it with: ```javascript var utils = require('utils'); var async = utils.async; ``` 'use strict' utils = null assert = console.assert.bind console {exports} = module {shift} = Array:: {isArray} = Array NOP = -> ## utils.async.forEach(*NotPrimitive* array, *Function* callback, [*Function* onEnd, *Any* context]) This is an asynchronous version of the standard `Array.prototype.forEach()` function which works with arrays and objects. The given callback function is called with parameters: - if an array given: element value, index, array, next callback - if an object given: key, value, object, next callback ```javascript var toLoadInOrder = ['users.json', 'families.js', 'relationships.js']; utils.async.forEach(toLoadInOrder, function(elem, i, array, next){ console.log("Load " + elem + " file"); // on load end ... next(); }, function(){ console.log("All files are loaded!"); }); // Load users.json // Load families.json // load relationships.json // All files are loaded! ``` forEach = do -> forArray = (arr, callback, onEnd, thisArg) -> i = 0 n = arr.length next = -> # return and call onEnd if there is no elements to check if i is n then return onEnd.call thisArg # increase counter i++ # call callback func callback.call thisArg, arr[i - 1], i - 1, arr, next # start next() forObject = (obj, callback, onEnd, thisArg) -> keys = Object.keys obj i = 0 n = keys.length next = -> # return and call onEnd if there is no pairs to check if i is n return onEnd.call thisArg # call callback func key = keys[i] callback.call thisArg, key, obj[key], obj, next # increase counter i++ # start next() (list, callback, onEnd, thisArg) -> assert not utils.isPrimitive list assert typeof callback is 'function' assert typeof onEnd is 'function' if onEnd? method = if isArray list then forArray else forObject method list, callback, onEnd, thisArg null ## **Class** Stack() Stores functions and runs them synchronously or asynchronously. ```javascript var stack = new utils.async.Stack; function load(src, callback){ console.log("Load " + src + " file"); // load file async ... // first callback parameter is an error ... callback(null, "fiel data"); }; stack.add(load, null, ['items.json']); stack.add(load, null, ['users.json']); stack.runAllSimultaneously(function(){ console.log("All files have been loaded!"); }); // Load items.json file // Load users.json file // All files have been loaded! // or ... (simultaneous call has no order) // Load users.json file // Load items.json file // All files have been loaded! ``` class Stack constructor: -> # One-deep array of added functions # in schema [function, context, args, ...] @_arr = [] @length = 0 @pending = false Object.preventExtensions @ ### Stack::add(*Function* function, [*Any* context, *NotPrimitive* arguments]) Adds the given function to the stack. The function must provide a callback argument as the last argument. The first argument of the callback function is always an error. ```javascript var stack = new utils.async.Stack; function add(a, b, callback){ if (isFinite(a) && isFinite(b)){ callback(null, a+b); } else { throw "Finite numbers are required!"; } } stack.add(add, null, [1, 2]); stack.runAll(function(err, result){ console.log(err, result); }); // null 3 stack.add(add, null, [1, NaN]); stack.runAll(function(err, result){ console.log(err, result); }); // "Finite numbers are required!" undefined ``` add: (func, context, args) -> assert utils.isObject args if args? @_arr.push func, context, args @length++ @ ### Stack::callNext([*Array* arguments], *Function* callback) Calls the first function from the stack and remove it. callNext: (args, callback) -> if typeof args is 'function' and not callback? callback = args args = null assert typeof callback is 'function' # on empty unless @_arr.length return callback() # get next @length-- func = @_arr.shift() context = @_arr.shift() funcArgs = @_arr.shift() if typeof func is 'string' func = utils.get context, func if typeof func isnt 'function' throw new TypeError 'ASync Stack::callNext(): ' + 'function to call is not a function' funcLength = func.length or Math.max( args?.length or 0, funcArgs?.length or 0 ) + 1 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?.stack or syncError}`" assert not called , "Callback can't be called twice;\nFunction: `#{func}`" called = true callback.apply @, arguments # add callback into funcArgs # To avoid got funcArgs array modification and to minimise memory usage, # we create a new object with the `funcArgs` as a prototype. # `Function::apply` expects an object and iterate by it to the `length`. funcArgs = Object.create(funcArgs or null) funcArgs[funcLength - 1] = callbackWrapper if funcArgs.length is undefined or funcArgs.length < funcLength funcArgs.length = funcLength if args for arg, i in args if i isnt funcLength - 1 and funcArgs[i] is undefined funcArgs[i] = arg # call; support sync errors syncError = utils.catchError func, context, funcArgs if syncError callbackWrapper syncError null ### Stack::runAll([*Function* callback, *Any* callbackContext]) Calls all functions from the stack one by one. When an error occurs, processing stops and the callback function is called with the got error. runAll: (callback = NOP, ctx = null) -> assert @pending is false if typeof callback isnt 'function' throw new TypeError 'ASync runAll(): ' + 'passed callback is not a function' unless @_arr.length return callback.call ctx, null onNextCalled = (err, args...) => # on err if err? @pending = false return callback.call ctx, err # call next if @_arr.length return callNext args @pending = false callback.apply ctx, arguments callNext = (args) => @callNext args, onNextCalled @pending = true callNext() null ### Stack::runAllSimultaneously([*Function* callback, *Any* callbackContext]) Calls all functions from the stack simultaneously (all at the same time). When an error occurs, processing stops and the callback function is called with the got error. runAllSimultaneously: (callback = NOP, ctx = null) -> assert @pending is false assert typeof callback is 'function' length = n = @_arr.length / 3 done = 0 unless length return callback.call(ctx) onDone = (err) => ++done if done > length return if err done = length @pending = false return callback.call ctx, err if done is length @pending = false callback.call(ctx) # run all functions @pending = true while n-- @callNext onDone null ### Exports ### module.exports = -> [utils] = arguments utils.async = forEach: forEach Stack: Stack