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