Skip to content

Commit

Permalink
Added decorator option for the .promisify method;
Browse files Browse the repository at this point in the history
Added optional `scope` argument for decorated plain and ECMA async functions;
  • Loading branch information
DigitalBrainJS committed Apr 18, 2021
1 parent fe847dc commit 63e7895
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 11 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,16 @@ If value is a number it will be considered as the value for timeout option If va
| result | <code>\*</code> |
| scope | <code>CPromise</code> |

<a name="module_CPromise..CPPromisifyDecoratorFn"></a>

### CPromise~CPPromisifyDecoratorFn ⇒ <code>function</code>
**Kind**: inner typedef of [<code>CPromise</code>](#module_CPromise)

| Param | Type | Description |
| --- | --- | --- |
| originalFn | <code>function</code> | function to decorate |
| options | <code>PromisifyOptions</code> | |

<a name="module_CPromise..PromisifyOptions"></a>

### CPromise~PromisifyOptions : <code>Object</code>
Expand All @@ -1823,6 +1833,7 @@ If value is a number it will be considered as the value for timeout option If va
| [finalize] | <code>PromisifyFinalizeFn</code> | aggregate all passed arguments to an array |
| [fnType] | <code>&quot;plain&quot;</code> \| <code>&quot;generator&quot;</code> \| <code>&quot;async&quot;</code> | |
| [scopeArg] | <code>boolean</code> | pass the CPromise scope as the first argument to the generator function |
| [decorator] | <code>function</code> | CPPromisifyDecoratorFn |


## License
Expand Down
47 changes: 36 additions & 11 deletions lib/c-promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -1554,12 +1554,20 @@ class CPromise extends Promise {
* @param {CPromise} scope
*/

/**
* @typedef {function} CPPromisifyDecoratorFn
* @param {function} originalFn function to decorate
* @param {PromisifyOptions} options
* @returns {function}
*/

/**
* @typedef {Object} PromisifyOptions
* @property {Boolean} [multiArgs] aggregate all passed arguments to an array
* @property {PromisifyFinalizeFn} [finalize] aggregate all passed arguments to an array
* @property {"plain"|"generator"|"async"} [fnType]
* @property {boolean} [scopeArg] pass the CPromise scope as the first argument to the generator function
* @property {function} [decorator] CPPromisifyDecoratorFn
*/

/**
Expand All @@ -1581,26 +1589,37 @@ class CPromise extends Promise {
multiArgs: boolean,
finalize: functionPlain,
fnType: string,
scopeArg: boolean
scopeArg: boolean,
decorator: functionPlain
});
}

const {multiArgs, finalize, fnType, scopeArg} = options || {};
const {multiArgs, finalize, fnType= getFnType(originalFn), scopeArg, decorator} = options || {};
const context = this;

switch (fnType || getFnType(originalFn)) {
const decoratedFn = decorator && decorator.call(context, originalFn, {
...options,
fnType
}) || originalFn;

switch (fnType) {
case "plain":
return (...args) => {
return new this((resolve, reject, scope) => {
const result = originalFn.apply(this, [...args, (err, ...data) => {
return function (...args) {
return new context((resolve, reject, scope) => {
const callback = (err, ...data) => {
if (err) {
return reject(err);
}

return multiArgs || data.length > 1 ? resolve(data) : resolve(data[0]);
}]);
};

if(isThenable(result)){
const result = decoratedFn.apply(
this,
scopeArg ? [scopeArg, ...args, callback] : [...args, callback]
);

if (isThenable(result)) {
result.then(resolve, reject);
}

Expand All @@ -1609,15 +1628,21 @@ class CPromise extends Promise {
}
case "generator":
return function (...args) {
return context.run(originalFn, {
return context.run(decoratedFn, {
context: this,
scopeArg,
args
});
}
case "async":
return function () {
return context.from(originalFn.apply(this, arguments), {resolveSignatures: false});
return function (...args) {
if(!scopeArg){
return context.resolve(decoratedFn.apply(this, args));
}

return context.resolve().then((v, scope)=>{
return context.resolve(decoratedFn.apply(this, [scope, ...args]));
})
}
}

Expand Down
18 changes: 18 additions & 0 deletions test/tests/CPromise.js
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,24 @@ module.exports = {
return promise.then(value => {
assert.deepStrictEqual(value, 123);
})
},

'should support decorator option': async function(){
CPromise.promisify(function* (arg0) {
assert.strictEqual(arg0, 8);
return yield CPromise.delay(100, 123);
}, {
decorator: (fn, options) => {
return function(scope, arg0, arg1){
assert.ok(scope instanceof CPromise, 'scope is not a CPromise context');
assert.strictEqual(options.fnType, 'generator');
return fn(arg0 + arg1);
};
},
scopeArg: true
})(3, 5).then(v => {
assert.strictEqual(v, 123);
});
}
},

Expand Down

0 comments on commit 63e7895

Please sign in to comment.