Reputation: 835
I am trying to create a decorator in node js to log entry and exit from a function when it is being executed for example
Executing ServiceClass.sampleService with params 5,1
then after execution is finished Execution of ServiceClass.sampleService completed with success/error
This works fine for normal functions which return number or string or array or some object literals, but when I want to decorate a function which returns promise or callback it logs details but the values are not returned where the function is invoked.
Here is my sample code :
logger.ts (contains decorator function)
export function performanceLog(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function (...args) {
console.log(`Executing ${target.constructor.name}.${name} with parameters ${args}`);
// Execute function like a function with promise
let start: any = new Date();
return original.apply(this, args).then(data => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed successfully in ${end / 1000} seconds`);
}).catch(error => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed with error in ${end / 1000} seconds`);
});
}
}
return descriptor;
}
sampleService.ts (Regular Service class)
import { performanceLog } from '../utilities/logger';
let err = false;
export default class SampleService {
@performanceLog
public sum(a, b) {
return new Promise((resolve, reject) => {
if (err) {
reject(a - b);
} else {
resolve(a + b);
}
})
}
}
const e = new SampleService();
e.sum(51, 6).then(data => console.log("## Data: ", data)).catch(err => console.log("##err: ", err));
// Here data is undefined and it never goes into the catch part either
What am I missing here?
EDIT
The similar problem exits when I have a function returning callback function
export default class SampleService {
@performanceLog
public sum(a, b, callback) {
// return new Promise((resolve, reject) => {
if (err) {
return callback(a - b, null);
} else {
return callback(null, a+b);
}
// })
}
}
const e = new SampleService();
// e.sum(51, 6).then(data => console.log("## Data: ", data)).catch(err => console.log("##err: ", err));
e.sum(51, 6, function (err, res) {
console.log(`This is err ${err} and res ${res}`);
});
Upvotes: 0
Views: 1488
Reputation: 5199
This is because you are overriding the Promise
value. The value returned in then
or catch
is propagated to the rest of the Promise chain.
If you just want to "spy" on the Promise
then return
/throw
the value or don't return a new Promise
. Example :
const promise = original.apply(this, args)
promise.then(data => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed successfully in ${end / 1000} seconds`);
}).catch(error => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed with error in ${end / 1000} seconds`);
});
return promise
OR
return original.apply(this, args).then(data => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed successfully in ${end / 1000} seconds`);
// propagate the return value
return data;
}).catch(error => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed with error in ${end / 1000} seconds`);
// propagate the error value
throw error
});
Upvotes: 1
Reputation: 7574
You said it yourself, the values are not returned when the function is invoked :-)
You are executing the original promise in the performanceLog
decorator, and then do your performance measuring in the then
callback. Here you pass the value from the original function, but don't return to the outside. Imagine your Promise callstack to be something like this:
sum(51,6)
.then(yourPromiseLog)
.then(theCallbackFromOutside)
The solution is to return the value in your decorator again:
return original.apply(this, args).then(data => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed successfully in ${end / 1000} seconds`);
return data;
})
Similarly, you have to throw the error again in the catch callback:
catch(error => {
let end: any = new Date();
end = end-start;
console.log(`\nExecution of ${target.constructor.name}.${name} completed with error in ${end / 1000} seconds`);
return Promise.reject(error);
});
Hope this helps!
Upvotes: 1