| 'use strict' |
| |
| var slice = [].slice |
| |
| module.exports = wrap |
| |
| // Wrap `fn`. |
| // Can be sync or async; return a promise, receive a completion handler, return |
| // new values and errors. |
| function wrap(fn, callback) { |
| var invoked |
| |
| return wrapped |
| |
| function wrapped() { |
| var params = slice.call(arguments, 0) |
| var callback = fn.length > params.length |
| var result |
| |
| if (callback) { |
| params.push(done) |
| } |
| |
| try { |
| result = fn.apply(null, params) |
| } catch (error) { |
| // Well, this is quite the pickle. |
| // `fn` received a callback and invoked it (thus continuing the pipeline), |
| // but later also threw an error. |
| // We’re not about to restart the pipeline again, so the only thing left |
| // to do is to throw the thing instead. |
| if (callback && invoked) { |
| throw error |
| } |
| |
| return done(error) |
| } |
| |
| if (!callback) { |
| if (result && typeof result.then === 'function') { |
| result.then(then, done) |
| } else if (result instanceof Error) { |
| done(result) |
| } else { |
| then(result) |
| } |
| } |
| } |
| |
| // Invoke `next`, only once. |
| function done() { |
| if (!invoked) { |
| invoked = true |
| |
| callback.apply(null, arguments) |
| } |
| } |
| |
| // Invoke `done` with one value. |
| // Tracks if an error is passed, too. |
| function then(value) { |
| done(null, value) |
| } |
| } |