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
3 Answers
Reset to default 5There are many problems with this:
new Promise
will invokePromise
, not the overriddenPromise.prototype.constructor
.- Arrow functions are no constructors. They will throw when being called with
new
. - Calling
_constructor
(i.e. the builtinPromise
) withoutnew
will throw - Arrow functions are no methods. They have a lexical
this
value, not a dynamic receiver. - You want to
apply
theargs
, notcall
them, or use spread syntax
You can solve these by using function
s 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()