| '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; |
| } |
| }; |
| } |