"this" is undefined when function is passed as argument in chain [duplicate] - javascript

This question already has answers here:
Object method with ES6 / Bluebird promises
(2 answers)
Closed 7 years ago.
Simple example of my problem
Consider the following situation, where I have a few functions (a and b) that I used in a promise-chain in c:
class SomeClass {
constructor(){
this.v1 = 1;
this.v2 = 2;
}
a() {
return new Promise((resolve, reject) => {
console.log('a', this.v1); // Do something with `this`
resolve();
});
}
b() {
return new Promise((resolve, reject) => {
console.log('b', this.v2); // Do something with `this`
resolve();
});
}
c() {
return this.a().then(this.b); // passing b as argument
}
}
When I call c and run the chain of promises, this is undefined in b.
const sc = new SomeClass();
sc.c().then(() =>{
console.log('done')
}).catch((error) => {
console.log('error', error);
});
Output:
a 1
error [TypeError: Cannot read property 'v2' of undefined]
I know that arrow functions inherit the outer this, but I am not sure why it is undefined since I am invoking it from c.

The problem is here:
this.a().then(this.b)
because the resulting function retrieved by this.b becomes unbound from this (you don't actually invoke it in the way stated in your question).
You can solve it in a way that is consistent with the rest of your code, by using an arrow method to preserve scope:
this.a().then(obj => this.b(obj))
or you can use .bind to achieve a similar result:
this.a().then(this.b.bind(this))

Related

Differentiate function and constructor in runtime [duplicate]

This question already has answers here:
How do you check the difference between an ECMAScript 6 class and function?
(9 answers)
How to check if a Javascript function is a constructor
(9 answers)
Closed 6 months ago.
In TypeScript, I'm writing a function that takes an Error factory as an argument: either a class name or a factory function. Something like the below:
// Alias from class-transformer package
type ClassConstructor<T> = {
new (...args: any[]): T;
};
function doSomething(value: number, errorFactory: () => Error | ClassConstructor<Error>) {
if (value === 0) {
// Zero is not allowed
if (/* errorFactory is a class constructor */) {
throw new errorFactory()
} else {
throw errorFactory()
}
}
}
In the above function, errorFactory can be an Error class, such that the following code works:
doSomething(0, Error);
Or it can be a function that creates an Error:
doSomething(0, () => new Error());
The issue is that ClassConstructor is a TypeScript type, so it doesn't survive compilation to JavaScript.
typeof(Error) is function, and so is typeof(()=>{}).
So how to determine the parameter's type? What should be the test in /* errorFactory is a class constructor */?
By the above code, I understood you need to invoke the constructor with new, and the function without new.
In the above example, Error actually can be invoked without new, it can be called just by typing:
throw Error(msg); // msg: string = The error message
The code below can be used for testing if the function (or constructor) must be called with new:
// Returns:
// - True if the 'func' must be called with 'new'
// - False if the 'func' can be called without 'new'
function isConstructor(func) {
try {
// Invoke without 'new'
func();
}
catch (err) {
if (/^TypeError:.*?constructor/.test(err.toString())) {
// The error is about that it isn't a normal function
return true;
}
else {
// The error isn't about that; let's throw it
throw new err.constructor(err.toString().substr(err.toString().indexOf(" ") + 1), {cause: err});
}
}
return false;
}
// Let's run some tests
console.log(isConstructor("abc".constructor)); // False
// String() returns empty string; it's not a constructor
console.log(isConstructor(() => {})); // False
// Arrow function
console.log(isConstructor(class {})); // True
// This *is* a cliss
console.log(isConstructor(Image)); // True
// Some built-in cannot be invoked without 'new'
console.log(isConstructor(throwErr)); // TypeError: Another error
function throwErr() {
throw new TypeError("Another error");
}

Strange behaviour of Javascript `new` from `function` [duplicate]

This question already has answers here:
Using Object constructors that return objects. How it works?
(2 answers)
What values can a constructor return to avoid returning this?
(6 answers)
What is returned from a constructor?
(7 answers)
Closed 2 years ago.
I've just run into something that really surprises me. Consider the following four functions:
function A() {
this.q = 1;
}
function B() {
this.q = 1;
return this;
}
function C() {
this.q = 1;
return 42;
}
function D() {
this.q = 1;
return {};
}
and let's create objects (via new) from all of them:
console.log('a', new A());
console.log('b', new B());
console.log('c', new C());
console.log('d', new D());
This is the output:
a A { q: 1 }
b B { q: 1 }
c C { q: 1 }
d {}
First three seem to indicate that it doesn't matter what the function returns, that JS only cares about what each function does with this (which was my previous believe, btw). But the last one contradicts that.
So, what's happening here? My revised rule would be "if the function returns an Object, we keep that. Otherwise, we keep this". But I feel quite unsure about it.
in JavaScript when you invoke a function without new like test() it just runs the function and it. returns whatever is being returned by the function
while when you invoke a function with new like new test() it expects the return statement to return an object else return this object.
so
function test() {
this.q = 1
}
test(); // return undefined
new test(); // return {q:1}
When you use the new operator it creates an object.
The return value is as expected. If in the return it is an object will return that object otherwise if the return it is a function will return that function.
function A(){
this.b = 3;
return function(){return 4;};
}
function B(){
this.b = 3;
return { d:4};
}
console.log(typeof (new A())); //function
console.log(typeof (new B())); //object {d:4}
If any other value or non-existent in the return, this operator will take precedence returned as an object.
function C(){
this.b = 3;
return "78";
}
console.log(typeof (new C())); // object {b:3}

Calling class function from callback function calls in nodejs [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 4 years ago.
I have this situation
class A {
a(params) {
//some code here
}
b(params) {
//some code here
}
c(params) {
this.a(function(data) {
console.log(this); // undefined
this.b(); // error no function b of undefined
})
}
}
I have tried binding this to 'a' using bind(this) but it says Cannot read property 'bind' of undefined or this is not defined. When I print this, I get class A. I want to call it inside 'a' function.
When you have defined a new function, the meaning of this has been changed inside it. You either need to use an arrow function:
this.a((data) => {
console.log(this); // class A
this.b();
})
or save the reference of this in a local variable:
var self = this;
this.a(function(data){
console.log(self); // class A
self.b();
})
Not sure at which point you are expecting the "b" method execution. I've added jsFiddle here: https://jsfiddle.net/k3jkobae/ and just saw that there was short correct answer while I was wrapping mine :) The arrow function is best suited.
class A {
a( callback ){
console.log('a');
callback();
}
b(params){
console.log('b');
}
c(params) {
this.a( () => this.b() );
}
}
const myClass = new A();
myClass.c();

JavaScript - Using "this" with chained promises [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I am writing some Node code with ES6. In my code, I have a class, that looks like the following. Please note, this is a basic example intended to isolate and show the problem.
class MyClass {
constructor() {
this.id = uuid();
this.number = Math.floor((Math.random() * 100) + 1);
}
process() {
this.validateNumber()
.then(this.generateImage)
.catch(function(err) {
console.log(err);
})
;
}
validateNumber() {
let self = this;
var promise = new Promise(function(resolve, reject) {
// do stuff with self.number
resolve({});
});
}
generateImage() {
console.log(this);
// the above prints "undefined" in the console window
// how do I get this.number again?
}
}
In this class, you'll notice that I generate a random number in my constructor. I want that number to be usable throughout the methods in my class. However, since I have one method that chains together promises, it's like this loses meaning after validateNumber is executed.
how do I resolve this?
ES6 introduced a feature called arrow functions. Apart from the syntax being more concise than the conventional function syntax, it retains the context of this. Essentially, your code can be changed to the following:
var promise = new Promise((resolve, reject) => {
console.log(this); // MyClass
resolve({});
});
And you also need to retain the context inside the then
this.validateNumber()
.then(() => this.generateImage());

Unexpected variables in arguments[] when passing a function

I have two files in a folder - index.js and util.js with their code base as follows
Util.js
let obj = {}
obj.sendTransaction = () => {
console.log(arguments);
return new Promise((resolve, reject) => {
// try {
// let data = ethFunction.call()
// resolve(data)
// } catch (e) {
// reject(e)
// }
});
}
module.exports = obj
In Index.js, if I pass arguments to addNewParticipant or its variation then they do not turn up in the arguments object in util.js, for instance
const addNewParticipant = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction()
}
const addNewParticipantTwo = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction(ethFunction, foo, bar)
}
and call it such addNewParticpant(1, 2) and , addNewParticpantNew(1, 2) the numbers 1 and 2 do not show up in the arguments object in the util function. In fact, the arguments object remains the same, 4 inputs describing some functions and files in node_modules including Bluebird and a reference to index.js itself
My final aim is to
Pass a function from index.js to util.js
Pass along unknown number of variables
Call the passed function and apply the unknown number of variables to it
Wrap the whole thing in a promise and do some data validation
Ideally arguments[0] would represent a function I would pass and the other would be the values. I would then use
var result = arguments[0].apply(null, Array().slice.call(arguments, 1));
If it helps, the function I want to pass has an optional callback feature
As already mentioned in the comment, fat arrows don't have their own this or arguments objects. The arguments object you're logging is from the function created by the module loader, and its passed arguments.
You can either use a "regular function", or in this case, you can use a ...rest parameter
And, avoid the Deferred antipattern.
//first a little utility that might be handy in different places:
//casts/converts a value to a promise,
//unlike Promise.resolve, passed functions are executed
var promise = function(value){
return typeof value === "function"?
this.then( value ):
Promise.resolve( value );
}.bind( Promise.resolve() );
module.exports = {
sendTransaction(fn, ...args){
return promise(() => fn.apply(null, args));
}
}

Categories