index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**!
  2. * node-thunkify-wrap - index.js
  3. * MIT Licensed
  4. */
  5. 'use strict';
  6. var EventEmitter = require('events').EventEmitter;
  7. var enable = require('enable');
  8. /**
  9. * Expose `thunkify()`.
  10. * @param {Function | Object} input
  11. * @param {Object} [ctx]
  12. * @param {Array} [methods]
  13. * @return {Function}
  14. * @api public
  15. */
  16. module.exports = function (input, ctx, methods) {
  17. return wrapify(input, ctx, methods, thunkify);
  18. };
  19. function wrapify(input, ctx, methods, wrapfn) {
  20. var type = typeof input;
  21. // thunkify function
  22. if (type === 'function') {
  23. return wrapfn(input, ctx);
  24. }
  25. // thunkify object
  26. if (type === 'object') {
  27. if (Array.isArray(ctx)) {
  28. methods = ctx;
  29. ctx = input;
  30. }
  31. if (typeof ctx === 'string') {
  32. methods = [ctx];
  33. ctx = input;
  34. }
  35. ctx = ctx === undefined ? input : ctx;
  36. if (methods && methods.length) {
  37. methods.forEach(function (method) {
  38. input[method] = wrapfn(input[method], ctx);
  39. });
  40. } else {
  41. // thunkify all methods in input
  42. for (var key in input) {
  43. if (typeof input[key] === 'function') {
  44. input[key] = wrapfn(input[key], ctx);
  45. }
  46. }
  47. }
  48. return input;
  49. }
  50. throw new TypeError('thunkify accept only `function` or `object`');
  51. }
  52. var slice = Array.prototype.slice;
  53. /**
  54. * Wrap a regular callback `fn` as a thunk.
  55. *
  56. * @param {Function} fn
  57. * @param {Object} [ctx]
  58. * @return {Function}
  59. */
  60. function thunkify(fn, ctx) {
  61. if (!fn) {
  62. return fn;
  63. }
  64. if (isGeneratorFunction(fn)) {
  65. return fn;
  66. }
  67. if (fn.toString() === thunk.toString()) {
  68. return fn;
  69. }
  70. function thunk() {
  71. var args = slice.call(arguments);
  72. var results;
  73. var called;
  74. var cb;
  75. args.push(function () {
  76. results = arguments;
  77. if (cb && !called) {
  78. called = true;
  79. cb.apply(this, results);
  80. }
  81. });
  82. fn.apply(ctx || this, args);
  83. return function (fn) {
  84. cb = fn;
  85. if (results && !called) {
  86. called = true;
  87. fn.apply(ctx || this, results);
  88. }
  89. };
  90. }
  91. return thunk;
  92. }
  93. /**
  94. * wrap a event object to a thunk
  95. * yield to wait the event emit `end`, `close`, `finish` or others
  96. * @param {Event} e
  97. * @param {Array} globalEvents
  98. * @return {Function}
  99. */
  100. module.exports.event = function (e, globalEvents) {
  101. globalEvents = globalEvents || ['end'];
  102. return function (endEvents) {
  103. var called = false;
  104. endEvents = endEvents || globalEvents;
  105. if (!Array.isArray(endEvents)) {
  106. endEvents = [endEvents];
  107. }
  108. return function (done) {
  109. // clean
  110. function _done(err, data) {
  111. if (called) {
  112. return;
  113. }
  114. called = true;
  115. e.removeListener('error', _done);
  116. endEvents.forEach(function (name) {
  117. e.removeListener(name, end);
  118. });
  119. done(err, data);
  120. }
  121. function end(data) {
  122. _done(null, data);
  123. }
  124. e.once('error', _done);
  125. endEvents.forEach(function (name) {
  126. e.once(name, end);
  127. });
  128. };
  129. };
  130. };
  131. if (enable.generator) {
  132. /**
  133. * Expose `genify()`.
  134. * @param {Function | Object} input
  135. * @param {Object} [ctx]
  136. * @param {Array} [methods]
  137. * @return {Function}
  138. * @api public
  139. */
  140. var genify = require('./genify')(thunkify);
  141. module.exports.genify = function (input, ctx, methods) {
  142. return wrapify(input, ctx, methods, genify);
  143. };
  144. }
  145. function isGeneratorFunction(fn) {
  146. return typeof fn === 'function' && fn.constructor.name === 'GeneratorFunction';
  147. }