promisify.js

  1. 'use strict';
  2. const Aigle = require('./aigle');
  3. const { INTERNAL, callThen } = require('./internal/util');
  4. const globalSetImmediate = typeof setImmediate === 'function' ? setImmediate : {};
  5. const custom =
  6. (() => {
  7. try {
  8. return require('util').promisify.custom;
  9. } catch (e) {
  10. return;
  11. }
  12. })() || {};
  13. module.exports = promisify;
  14. /**
  15. * @param {Object|Function} fn
  16. * @param {string|number|Object} [fn]
  17. * @param {Object} [fn.context]
  18. * @example
  19. * const func = (a, b, c, callback) => callback(null, a + b + c);
  20. * Aigle.promisify(func)(1, 2, 3)
  21. * .then(value => console.log(value)); // 6
  22. *
  23. * @example
  24. * const obj = {
  25. * val: 1,
  26. * get(callback) {
  27. * callback(null, this.val);
  28. * }
  29. * };
  30. *
  31. * // using bind
  32. * Aigle.promisify(obj.get.bind(obj))().then(console.log);
  33. *
  34. * // using context
  35. * Aigle.promisify(obj.get, { context: obj })().then(console.log);
  36. *
  37. * // using shorthand
  38. * Aigle.promisify(obj, 'get')().then(console.log);
  39. */
  40. function promisify(fn, opts) {
  41. switch (typeof fn) {
  42. case 'object':
  43. switch (typeof opts) {
  44. case 'string':
  45. case 'number':
  46. if (typeof fn[opts] !== 'function') {
  47. throw new TypeError('Function not found key: ' + opts);
  48. }
  49. if (fn[opts].__isPromisified__) {
  50. return fn[opts];
  51. }
  52. return makeFunctionByKey(fn, opts);
  53. default:
  54. throw new TypeError('Second argument is invalid');
  55. }
  56. case 'function':
  57. if (fn.__isPromisified__) {
  58. return fn;
  59. }
  60. const ctx = opts && opts.context !== undefined ? opts.context : undefined;
  61. return makeFunction(fn, ctx);
  62. default:
  63. throw new TypeError('Type of first argument is not function');
  64. }
  65. }
  66. /**
  67. * @private
  68. * @param {Aigle} promise
  69. */
  70. function makeCallback(promise) {
  71. return (err, res) => (err ? promise._reject(err) : promise._resolve(res));
  72. }
  73. /**
  74. * @private
  75. * @param {Object} obj
  76. * @param {string} key
  77. */
  78. function makeFunctionByKey(obj, key) {
  79. promisified.__isPromisified__ = true;
  80. return promisified;
  81. function promisified(arg) {
  82. const promise = new Aigle(INTERNAL);
  83. const callback = makeCallback(promise);
  84. let l = arguments.length;
  85. switch (l) {
  86. case 0:
  87. obj[key](callback);
  88. break;
  89. case 1:
  90. obj[key](arg, callback);
  91. break;
  92. default:
  93. const args = Array(l);
  94. while (l--) {
  95. args[l] = arguments[l];
  96. }
  97. args[args.length] = callback;
  98. obj[key].apply(obj, args);
  99. break;
  100. }
  101. return promise;
  102. }
  103. }
  104. /**
  105. * @private
  106. * @param {function} fn
  107. * @param {*} [ctx]
  108. */
  109. function makeFunction(fn, ctx) {
  110. const func = fn[custom];
  111. if (func) {
  112. nativePromisified.__isPromisified__ = true;
  113. return nativePromisified;
  114. }
  115. switch (fn) {
  116. case setTimeout:
  117. return Aigle.delay;
  118. case globalSetImmediate:
  119. return Aigle.resolve;
  120. }
  121. promisified.__isPromisified__ = true;
  122. return promisified;
  123. function nativePromisified(arg) {
  124. const promise = new Aigle(INTERNAL);
  125. let l = arguments.length;
  126. let p;
  127. switch (l) {
  128. case 0:
  129. p = func.call(ctx || this);
  130. break;
  131. case 1:
  132. p = func.call(ctx || this, arg);
  133. break;
  134. default:
  135. const args = Array(l);
  136. while (l--) {
  137. args[l] = arguments[l];
  138. }
  139. p = func.apply(ctx || this, args);
  140. break;
  141. }
  142. callThen(p, promise);
  143. return promise;
  144. }
  145. function promisified(arg) {
  146. const promise = new Aigle(INTERNAL);
  147. const callback = makeCallback(promise);
  148. let l = arguments.length;
  149. switch (l) {
  150. case 0:
  151. fn.call(ctx || this, callback);
  152. break;
  153. case 1:
  154. fn.call(ctx || this, arg, callback);
  155. break;
  156. default:
  157. const args = Array(l);
  158. while (l--) {
  159. args[l] = arguments[l];
  160. }
  161. args[args.length] = callback;
  162. fn.apply(ctx || this, args);
  163. break;
  164. }
  165. return promise;
  166. }
  167. }