You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							54 lines
						
					
					
						
							1.5 KiB
						
					
					
				
			
		
		
	
	
							54 lines
						
					
					
						
							1.5 KiB
						
					
					
				'use strict'; | 
						|
 | 
						|
let call = module.exports = { | 
						|
  safe: safeCall, | 
						|
  once: callOnce, | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Calls a function with the given arguments, and ensures that the error-first callback is _always_ | 
						|
 * invoked exactly once, even if the function throws an error. | 
						|
 * | 
						|
 * @param {function} fn - The function to invoke | 
						|
 * @param {...*} args - The arguments to pass to the function. The final argument must be a callback function. | 
						|
 */ | 
						|
function safeCall (fn, args) { | 
						|
  // Get the function arguments as an array | 
						|
  args = Array.prototype.slice.call(arguments, 1); | 
						|
 | 
						|
  // Replace the callback function with a wrapper that ensures it will only be called once | 
						|
  let callback = call.once(args.pop()); | 
						|
  args.push(callback); | 
						|
 | 
						|
  try { | 
						|
    fn.apply(null, args); | 
						|
  } | 
						|
  catch (err) { | 
						|
    callback(err); | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Returns a wrapper function that ensures the given callback function is only called once. | 
						|
 * Subsequent calls are ignored, unless the first argument is an Error, in which case the | 
						|
 * error is thrown. | 
						|
 * | 
						|
 * @param {function} fn - The function that should only be called once | 
						|
 * @returns {function} | 
						|
 */ | 
						|
function callOnce (fn) { | 
						|
  let fulfilled = false; | 
						|
 | 
						|
  return function onceWrapper (err) { | 
						|
    if (!fulfilled) { | 
						|
      fulfilled = true; | 
						|
      return fn.apply(this, arguments); | 
						|
    } | 
						|
    else if (err) { | 
						|
      // The callback has already been called, but now an error has occurred | 
						|
      // (most likely inside the callback function). So re-throw the error, | 
						|
      // so it gets handled further up the call stack | 
						|
      throw err; | 
						|
    } | 
						|
  }; | 
						|
}
 | 
						|
 |