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