node-extensions.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. 'use strict';
  2. // This file contains then/promise specific extensions that are only useful
  3. // for node.js interop
  4. var Promise = require('./core.js');
  5. module.exports = Promise;
  6. /* Static Functions */
  7. Promise.denodeify = function (fn, argumentCount) {
  8. if (
  9. typeof argumentCount === 'number' && argumentCount !== Infinity
  10. ) {
  11. return denodeifyWithCount(fn, argumentCount);
  12. } else {
  13. return denodeifyWithoutCount(fn);
  14. }
  15. };
  16. var callbackFn = (
  17. 'function (err, res) {' +
  18. 'if (err) { rj(err); } else { rs(res); }' +
  19. '}'
  20. );
  21. function denodeifyWithCount(fn, argumentCount) {
  22. var args = [];
  23. for (var i = 0; i < argumentCount; i++) {
  24. args.push('a' + i);
  25. }
  26. var body = [
  27. 'return function (' + args.join(',') + ') {',
  28. 'var self = this;',
  29. 'return new Promise(function (rs, rj) {',
  30. 'var res = fn.call(',
  31. ['self'].concat(args).concat([callbackFn]).join(','),
  32. ');',
  33. 'if (res &&',
  34. '(typeof res === "object" || typeof res === "function") &&',
  35. 'typeof res.then === "function"',
  36. ') {rs(res);}',
  37. '});',
  38. '};'
  39. ].join('');
  40. return Function(['Promise', 'fn'], body)(Promise, fn);
  41. }
  42. function denodeifyWithoutCount(fn) {
  43. var fnLength = Math.max(fn.length - 1, 3);
  44. var args = [];
  45. for (var i = 0; i < fnLength; i++) {
  46. args.push('a' + i);
  47. }
  48. var body = [
  49. 'return function (' + args.join(',') + ') {',
  50. 'var self = this;',
  51. 'var args;',
  52. 'var argLength = arguments.length;',
  53. 'if (arguments.length > ' + fnLength + ') {',
  54. 'args = new Array(arguments.length + 1);',
  55. 'for (var i = 0; i < arguments.length; i++) {',
  56. 'args[i] = arguments[i];',
  57. '}',
  58. '}',
  59. 'return new Promise(function (rs, rj) {',
  60. 'var cb = ' + callbackFn + ';',
  61. 'var res;',
  62. 'switch (argLength) {',
  63. args.concat(['extra']).map(function (_, index) {
  64. return (
  65. 'case ' + (index) + ':' +
  66. 'res = fn.call(' + ['self'].concat(args.slice(0, index)).concat('cb').join(',') + ');' +
  67. 'break;'
  68. );
  69. }).join(''),
  70. 'default:',
  71. 'args[argLength] = cb;',
  72. 'res = fn.apply(self, args);',
  73. '}',
  74. 'if (res &&',
  75. '(typeof res === "object" || typeof res === "function") &&',
  76. 'typeof res.then === "function"',
  77. ') {rs(res);}',
  78. '});',
  79. '};'
  80. ].join('');
  81. return Function(
  82. ['Promise', 'fn'],
  83. body
  84. )(Promise, fn);
  85. }
  86. Promise.nodeify = function (fn) {
  87. return function () {
  88. var args = Array.prototype.slice.call(arguments);
  89. var callback =
  90. typeof args[args.length - 1] === 'function' ? args.pop() : null;
  91. var ctx = this;
  92. try {
  93. return fn.apply(this, arguments).nodeify(callback, ctx);
  94. } catch (ex) {
  95. if (callback === null || typeof callback == 'undefined') {
  96. return new Promise(function (resolve, reject) {
  97. reject(ex);
  98. });
  99. } else {
  100. setImmediate(function () {
  101. callback.call(ctx, ex);
  102. })
  103. }
  104. }
  105. }
  106. };
  107. Promise.prototype.nodeify = function (callback, ctx) {
  108. if (typeof callback != 'function') return this;
  109. this.then(function (value) {
  110. setImmediate(function () {
  111. callback.call(ctx, null, value);
  112. });
  113. }, function (err) {
  114. setImmediate(function () {
  115. callback.call(ctx, err);
  116. });
  117. });
  118. };