Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

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

like image 904
Tarun Lalwani Avatar asked Mar 07 '23 01:03

Tarun Lalwani


1 Answers

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.

like image 146
Bergi Avatar answered Apr 12 '23 06:04

Bergi