Я пытаюсь создать декоратор в узле js для входа в журнал и выхода из функции, когда она выполняется, например Executing ServiceClass.sampleService with params 5,1 затем после завершения выполнения Execution of ServiceClass.sampleService completed with success/error

Это отлично работает для обычных функций, которые возвращают число, строку, массив или некоторые объектные литералы, но когда я хочу украсить функцию, которая возвращает обещание или обратный вызов, она регистрирует детали, но значения не возвращаются там, где функция вызывается.

Вот мой пример кода:

logger.ts (содержит функцию декоратора)

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 (класс регулярного обслуживания)

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

Что мне здесь не хватает?

ИЗМЕНИТЬ

Аналогичная проблема исчезает, когда у меня есть функция, возвращающая функцию обратного вызова

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}`);
});
0
TGW 13 Мар 2018 в 13:11

2 ответа

Лучший ответ

Это потому, что вы переопределяете значение Promise. Значение, возвращаемое в then или catch, распространяется на остальную часть цепочки обещаний.

Если вы просто хотите «шпионить» за Promise, тогда return / throw значение или не возвращайте новый Promise. Пример :

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

ИЛИ

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
});
1
fathy 13 Мар 2018 в 10:46

Вы сами сказали, что при вызове функции значения не возвращаются :-) Вы выполняете исходное обещание в декораторе performanceLog, а затем измеряете производительность в обратном вызове then. Здесь вы передаете значение из исходной функции, но не возвращаетесь наружу. Представьте, что ваш стек вызовов Promise выглядит примерно так:

sum(51,6)
  .then(yourPromiseLog)
  .then(theCallbackFromOutside)

Решение состоит в том, чтобы снова вернуть значение в вашем декораторе:

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;
})

Точно так же вы должны снова выдать ошибку в обратном вызове catch:

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);
});

Надеюсь это поможет!

1
ddprrt 13 Мар 2018 в 10:46