最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Unable to Override methods on Promise class - Promise.prototype.then called on incompatible receiver undefined - St

programmeradmin4浏览0评论

I am trying to override then, catch and finally functions. This is to create a global counter and monitor the pending promises.

The code needs to be executed Postman Sandbox, so I cannot use any NPM modules. I needs to do this with Native JS

Below is the code that I am trying to work out

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

But when I run this I get below error

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

TypeError: Method Promise.prototype.then called on inpatible 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._pile (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)

I am not sure what is wrong here, or is this the right way to do it or not.

I am trying to override then, catch and finally functions. This is to create a global counter and monitor the pending promises.

The code needs to be executed Postman Sandbox, so I cannot use any NPM modules. I needs to do this with Native JS

Below is the code that I am trying to work out

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

But when I run this I get below error

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

TypeError: Method Promise.prototype.then called on inpatible 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._pile (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)

I am not sure what is wrong here, or is this the right way to do it or not.

Share Improve this question asked Jan 29, 2018 at 10:09 Tarun LalwaniTarun Lalwani 147k11 gold badges213 silver badges276 bronze badges 1
  • this code is problematic , if you override "then" and call it inside your "then" then you wont be able to do await on the _then.call(this, args) as you will run an infinite recursive call. that means you cant get track of the pending promises as you function is sync without the ability to wait the async call. what you need to do is create a new function like "myThen" that warps then and not override it – Amit Wagner Commented Jan 29, 2018 at 11:13
Add a ment  | 

3 Answers 3

Reset to default 5

There are many problems with this:

  • new Promise will invoke Promise, not the overridden Promise.prototype.constructor.
  • Arrow functions are no constructors. They will throw when being called with new.
  • Calling _constructor (i.e. the builtin Promise) without new will throw
  • Arrow functions are no methods. They have a lexical this value, not a dynamic receiver.
  • You want to apply the args, not call them, or use spread syntax

You can solve these by using functions and the Reflect object properly, however I guess it's much simpler to use ES6 subclassing for this:

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.then(...args)
        console.log("then finished")
        return data;
    }
};

Of course, nothing here actually determines when a promise is pending or not.

In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object

this in different contexts

You need to change

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

into

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

@Tarun Lalwani, You cannot override promise constructor like above, as promise is a class not a function. As @Bergi mentioned, you need to use Reflect to intercept Promise constructor.

The issue here is that ES6 classes (e.g. Promise) are not callable, the way ES5 built in classes (e.g. Array). The emitted code that the piler produces uses super.call which throws in ES6 engine.

The fix here is to use Reflect.construct if it exists instead of call. Reflect.construct is not something we can polifill, but we can assume if built-in Promise is there, Refelect.construct is there too.

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

In the link above, it is been mentioned how to extend constructor. By doing so you can able to print console.log("Promise constructor called") when you call new Promise()

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论