'use strict';
const Aigle = require('./aigle');
const { INTERNAL, callThen } = require('./internal/util');
const globalSetImmediate = typeof setImmediate === 'function' ? setImmediate : {};
const custom =
(() => {
try {
return require('util').promisify.custom;
} catch (e) {
return;
}
})() || {};
module.exports = promisify;
/**
* @param {Object|Function} fn
* @param {string|number|Object} [fn]
* @param {Object} [fn.context]
* @example
* const func = (a, b, c, callback) => callback(null, a + b + c);
* Aigle.promisify(func)(1, 2, 3)
* .then(value => console.log(value)); // 6
*
* @example
* const obj = {
* val: 1,
* get(callback) {
* callback(null, this.val);
* }
* };
*
* // using bind
* Aigle.promisify(obj.get.bind(obj))().then(console.log);
*
* // using context
* Aigle.promisify(obj.get, { context: obj })().then(console.log);
*
* // using shorthand
* Aigle.promisify(obj, 'get')().then(console.log);
*/
function promisify(fn, opts) {
switch (typeof fn) {
case 'object':
switch (typeof opts) {
case 'string':
case 'number':
if (typeof fn[opts] !== 'function') {
throw new TypeError('Function not found key: ' + opts);
}
if (fn[opts].__isPromisified__) {
return fn[opts];
}
return makeFunctionByKey(fn, opts);
default:
throw new TypeError('Second argument is invalid');
}
case 'function':
if (fn.__isPromisified__) {
return fn;
}
const ctx = opts && opts.context !== undefined ? opts.context : undefined;
return makeFunction(fn, ctx);
default:
throw new TypeError('Type of first argument is not function');
}
}
/**
* @private
* @param {Aigle} promise
*/
function makeCallback(promise) {
return (err, res) => (err ? promise._reject(err) : promise._resolve(res));
}
/**
* @private
* @param {Object} obj
* @param {string} key
*/
function makeFunctionByKey(obj, key) {
promisified.__isPromisified__ = true;
return promisified;
function promisified(arg) {
const promise = new Aigle(INTERNAL);
const callback = makeCallback(promise);
let l = arguments.length;
switch (l) {
case 0:
obj[key](callback);
break;
case 1:
obj[key](arg, callback);
break;
default:
const args = Array(l);
while (l--) {
args[l] = arguments[l];
}
args[args.length] = callback;
obj[key].apply(obj, args);
break;
}
return promise;
}
}
/**
* @private
* @param {function} fn
* @param {*} [ctx]
*/
function makeFunction(fn, ctx) {
const func = fn[custom];
if (func) {
nativePromisified.__isPromisified__ = true;
return nativePromisified;
}
switch (fn) {
case setTimeout:
return Aigle.delay;
case globalSetImmediate:
return Aigle.resolve;
}
promisified.__isPromisified__ = true;
return promisified;
function nativePromisified(arg) {
const promise = new Aigle(INTERNAL);
let l = arguments.length;
let p;
switch (l) {
case 0:
p = func.call(ctx || this);
break;
case 1:
p = func.call(ctx || this, arg);
break;
default:
const args = Array(l);
while (l--) {
args[l] = arguments[l];
}
p = func.apply(ctx || this, args);
break;
}
callThen(p, promise);
return promise;
}
function promisified(arg) {
const promise = new Aigle(INTERNAL);
const callback = makeCallback(promise);
let l = arguments.length;
switch (l) {
case 0:
fn.call(ctx || this, callback);
break;
case 1:
fn.call(ctx || this, arg, callback);
break;
default:
const args = Array(l);
while (l--) {
args[l] = arguments[l];
}
args[args.length] = callback;
fn.apply(ctx || this, args);
break;
}
return promise;
}
}