【问题标题】:Decorator function returning undefined when using promises or callbacks使用 Promise 或回调时返回未定义的装饰器函数
【发布时间】:2018-03-13 10:11:16
【问题描述】:

我正在尝试在节点 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}`);
});

【问题讨论】:

    标签: javascript typescript decorator typescript-decorator


    【解决方案1】:

    这是因为您覆盖了 Promise 值。 thencatch 中返回的值会传播到 Promise 链的其余部分。

    如果您只是想“窥探”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
    });
    

    【讨论】:

    • 谢谢,你的第一个方法似乎更干净。但是我不能再次使用这种方法在变量中存储回调(即函数返回一个),因为它总是有未定义的
    • 我不太明白,你的意思是在装饰函数返回函数的情况下?
    • 这是一个简单的错误第一个回调函数,我一直期待它作为最后一个参数。我试图承诺它然后返回它,但它完全没有打印出来
    • 使用Promise.resolve(original.apply(this, args))。如果original.apply(this, args)Promise,它将返回它,否则它返回一个带有值的已解决承诺。但它不会等待callback 被调用,这是另一个问题?
    • 哦,这是个问题?现在我设置了一个超时来检查它的效果
    【解决方案2】:

    你自己说过,调用函数时不会返回值:-) 您正在 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);
    });
    

    希望这会有所帮助!

    【讨论】:

    • 感谢您的解释,调用堆栈的思路清晰了:)
    猜你喜欢
    • 2019-09-02
    • 2020-04-28
    • 2020-10-03
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 2021-10-22
    • 2019-03-24
    • 1970-01-01
    相关资源
    最近更新 更多