| var wrappy = require('wrappy') |
| var reqs = Object.create(null) |
| var once = require('once') |
| |
| module.exports = wrappy(inflight) |
| |
| function inflight (key, cb) { |
| if (reqs[key]) { |
| reqs[key].push(cb) |
| return null |
| } else { |
| reqs[key] = [cb] |
| return makeres(key) |
| } |
| } |
| |
| function makeres (key) { |
| return once(function RES () { |
| var cbs = reqs[key] |
| var len = cbs.length |
| var args = slice(arguments) |
| |
| // XXX It's somewhat ambiguous whether a new callback added in this |
| // pass should be queued for later execution if something in the |
| // list of callbacks throws, or if it should just be discarded. |
| // However, it's such an edge case that it hardly matters, and either |
| // choice is likely as surprising as the other. |
| // As it happens, we do go ahead and schedule it for later execution. |
| try { |
| for (var i = 0; i < len; i++) { |
| cbs[i].apply(null, args) |
| } |
| } finally { |
| if (cbs.length > len) { |
| // added more in the interim. |
| // de-zalgo, just in case, but don't call again. |
| cbs.splice(0, len) |
| process.nextTick(function () { |
| RES.apply(null, args) |
| }) |
| } else { |
| delete reqs[key] |
| } |
| } |
| }) |
| } |
| |
| function slice (args) { |
| var length = args.length |
| var array = [] |
| |
| for (var i = 0; i < length; i++) array[i] = args[i] |
| return array |
| } |