【问题标题】:Unable to Override methods on Promise class - Promise.prototype.then called on incompatible receiver undefined无法覆盖 Promise 类上的方法 - Promise.prototype.then 调用了不兼容的接收器未定义
【发布时间】:2018-07-07 23:58:20
【问题描述】:

我正在尝试覆盖 thencatchfinally 函数。这是为了创建一个全局计数器并监控未决的 Promise。

代码需要执行 Postman Sandbox,所以我不能使用任何 NPM 模块。我需要用 Native JS 来做这个

下面是我正在尝试解决的代码

_constructor = Promise.prototype.constructor
_then = Promise.prototype.then

Promise.prototype.constructor = (...args) => {
    console.log("Promise constructor called")
    let data = _constructor(...args)
    console.log("Promise constructor finished")
    return data;
}

Promise.prototype.then = (...args) => {
    console.log("then called")
    let data = _then.call(this, args)
    console.log("then finished")
    return data;
}

function test2(num) {
    let promise = new Promise((resolve, reject) => {
        if (num > 1) {
            setTimeout(()=> {
                resolve(num)
            }, 10)
        } else {
            reject(num);
        }
    });
    return promise
}

test2(10).then((num) => {
    console.log("Inside then")
    setTimeout(() => console.log("Promise has been resolved - " + num), 20);
})

但是当我运行它时,我得到以下错误

    let data = _then.call(this, args)
                     ^

TypeError: Method Promise.prototype.then called on incompatible receiver #<Object>
    at Object.then (<anonymous>)
    at Promise.then.args (/Users/tarun.lalwani/Desktop/test/postman/temp.jsx:15:22)
    at Object.<anonymous> (/Users/tarun.lalwani/test/postman/temp.jsx:33:11)
    at Module._compile (module.js:660:30)
    at Object.Module._extensions..js (module.js:671:10)
    at Module.load (module.js:573:32)
    at tryModuleLoad (module.js:513:12)
    at Function.Module._load (module.js:505:3)
    at Function.Module.runMain (module.js:701:10)
    at startup (bootstrap_node.js:193:16)

我不确定这里出了什么问题,或者这是否正确。

【问题讨论】:

  • 这段代码有问题,如果你覆盖“then”并在“then”中调用它,那么你将无法在 _then.call(this, args) 上等待,因为你将运行无限递归调用。这意味着您无法跟踪待处理的承诺,因为您的功能是同步的,而无法等待异步调用。你需要做的是创建一个像“myThen”这样的新函数,它会扭曲而不是覆盖它

标签: javascript node.js promise postman es6-promise


【解决方案1】:

这有很多问题:

  • new Promise 将调用 Promise,而不是覆盖的 Promise.prototype.constructor
  • 箭头函数不是构造函数。他们会在被new 调用时抛出。
  • 在没有new 的情况下调用_constructor(即内置的Promise)会抛出
  • 箭头函数不是方法。它们有一个词法 this 值,而不是动态接收器。
  • 你想applyargs,而不是call他们,或者使用扩展语法

您可以通过正确使用 functions 和 Reflect 对象来解决这些问题,但是我想为此使用 ES6 子类要简单得多:

Promise = class extends Promise {
    constructor(...args) {
        console.log("Promise constructor called")
        let data = super(...args)
        console.log("Promise constructor finished")
        return data;
    }
    then(...args) {
        console.log("then called")
        let data = super(...args)
        console.log("then finished")
        return data;
    }
};

当然,这里没有什么能真正决定一个 Promise 何时处于未决状态。

【讨论】:

  • 你说得对,之前的答案存在递归问题。我没有使用任何解决方案,因为它最终成为 Postman 中的 bug
【解决方案2】:

在箭头函数中, this 保留封闭词法的值 上下文是这样的。在全局代码中,它将被设置为全局对象

this in different contexts

你需要改变

Promise.prototype.constructor = (...args) => {
    console.log("Promise constructor called")
    let data = _constructor(...args)
    console.log("Promise constructor finished")
    return data;
}

Promise.prototype.then = (...args) => {
    console.log("then called")
    let data = _then.call(this, args)
    console.log("then finished")
    return data;
}

进入

Promise.prototype.constructor = function (...args) {
    console.log("Promise constructor called")
    let data = _constructor(...args)
    console.log("Promise constructor finished")
    return data;
}

Promise.prototype.then = function (...args) {
    console.log("then called")
    let data = _then.call(this, args)
    console.log("then finished")
    return data;
}

【讨论】:

  • 感谢then 功能现在可以工作,但console.log("Promise constructor called") 仍然没有被记录。我用过new Promise(...)。我在那部分缺少什么?
  • 正在阅读该链接,并将在此处发布该链接:-)。感谢您的帮助
【解决方案3】:

@Tarun Lalwani,你不能像上面那样重写 promise 构造函数,因为 promise 是一个类而不是一个函数。正如@Bergi 提到的,您需要使用 Reflect 来拦截 Promise 构造函数。

这里的问题是 ES6 类(例如 Promise)不可调用,就像 ES5 内置类(例如 Array)一样。编译器生成的发射代码使用 super.call,它在 ES6 引擎中抛出。

这里的修复是使用 Reflect.construct 如果它存在而不是调用。 Reflect.construct 不是我们可以 polifill 的东西,但我们可以假设如果有内置的 Promise,那么 Refelect.construct 也有。

https://github.com/Microsoft/TypeScript/issues/15202#issuecomment-297518643

在上面的链接中,提到了如何扩展构造函数。通过这样做,您可以在调用new Promise()时打印console.log("Promise constructor called")

【讨论】:

    猜你喜欢
    • 2019-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    相关资源
    最近更新 更多