JavaScript: Passing parameters to a callback function in "then" section - javascript

I'm trying to pass some parameter to a function used as callback, in "then" section. How can I do that?
In the following example, executionContext results as "undefined" in the callaback function.
function page_refresh(executionContext)
{
Xrm.Navigation.openAlertDialog({ text: "Now I'll refresh page"}).then(
function (success) {
var formContext = executionContext.getFormContext();
formContext.data.refresh(true);
},
function (error) {
console.log(error.message);
}
);
}

Related

TypeError: Cannot read properties of undefined (reading 'location') in res.redirect

I have the following function to retrieve an object from a database and extract an URL:
async redirect(id: string, redirectFunction: Function) {
if (!IdExists(id)) {
throw new Error(`ID ${id} does not exist.`);
}
const redirectLocation: string = await prisma.url.findUnique({
where: { uniqueId: id },
select: { url: true },
}).then((data) => {
return data?.url!;
});
redirectFunction('http://' + redirectLocation);
}
The function is called in the following segment of code:
app.get('/:id', async (req, res) => {
try {
redirectController.redirect(req.params.id, res.redirect);
} catch (error) {
console.error(error);
}
});
However, I get the TypeError: Cannot read properties of undefined (reading 'location'), I see that the error is related to the res.redirect method. However, when I replace it by console.log for debugging, the URL is showed properly. What may be causing this error?
This line of code:
redirectController.redirect(req.params.id, res.redirect);
Passes res.redirect (a function reference) as the second argument, but all that is passed is just the function so the res gets lost when you later try to call it. That causes the method to have a wrong this value when it executes and lots of things go wrong.
You can fix that several different ways. Once such way is with .bind():
redirectController.redirect(req.params.id, res.redirect.bind(res));
.bind() creates a small stub function that remembers the value of res so that when the stub function is called, it will be called with the right res reference and thus the this value inside the function will be correct.
Another way to solve it is to create your own little stub function:
redirectController.redirect(req.params.id, (...args) => {
res.redirect(...args);
});
When it calls your stub function, you call res.redirect() properly and pass it whatever arguments the controller called your stub function with.
As a small demonstration, you can see this effect here:
const obj = {
greeting: "Hello",
talk: function() {
if (this && this.greeting) {
console.log(`this.greeting is "${this.greeting}"`);
} else {
console.log("value of this is wrong");
}
}
}
console.log("calling as obj.talk()");
obj.talk(); // works
console.log("-------------------------");
// function we pass a method to and then call that method
function callTalk(fn) {
fn();
}
console.log("calling by passing method to another function");
callTalk(obj.talk); // doesn't work
// call it using .bind()
console.log("-------------------------");
console.log("calling using .bind()");
callTalk(obj.talk.bind(obj)); // works

I want to know in the 1st case the returned function is called as a middleware,,but in the 2nd case the returned function is not called

CASE1 ... where the returned function by validatebody(schemas.authschema) is executed as a middleware...
router.route("/signup").post(validatebody(schemas.authschema),userscontrollers.signup);
My 2nd file which exports the function and schema of validators...
module.exports={
validatebody:function(schema)
{
return function(req,res,next)
{
var result=joi.validate(req.body,schema);
if(result.error)
return res.status(400).json(result.error);
if(!req.value)
req.value={};
req.value['body']=result.value;
next();
}
},
schemas:{
authschema:joi.object().keys(
{
email:joi.string().email().required(),
password:joi.string().required()
})
}
};
NOW 2nd CASE....in this case I do not output anything and the page goes on loading for long because the function names second function is never called and next() is never triggered....but in the 1st case...the function which was returned was called..
app.get("/users",firstfunction,secondfunction);
function firstfunction()
{
return function(req,res,next)
{
console.log(req);
next();
}
}
function secondfunction(req,res,next)
{
res.send("go there");
}
app.listen(3000);
You're passing in firstfunction but not calling it. You would need to do
app.get("/users",firstfunction(),secondfunction);

Stubbing async.waterfall with Sinon.JS

I am trying to test async.waterfall by stubbing one of my functions using Sinon.js.
// functions.js
module.exports = {
// function I don't want to run
doBigThing: function() {
console.log("[doBigThing] was called");
},
// function I want to stub
myFunction: function(number, callback) {
console.log("[myFunction] was called");
doBigThing();
callback(null, number);
},
// function I want to test
waterfall: function(callback) {
return async.waterfall([
async.constant(5), // 5 just for the demo
myFunction
], callback);
}
}
And my test is:
describe('water', function() {
it ('successfully falls', function() {
// function under test
var waterfall = functions.waterfall;
var callback = function(err, number) {
expect(err).to.be.null;
expect(number).to.equal(5);
};
// I would like this stub to run instead of functions.myFunction
sinon.stub(functions, 'myFunction', function(number, callback) {
console.log("[myFunction] stub was called");
callback(null, number);
});
waterfall(callback);
// I suppose this is happening: myFunction(5, callback)
expect(functions.myFunction.withArgs(5, callback)).to.have.been.called;
expect(callback).to.have.been.called;
});
});
So the test passes, but the stub is ignored, because doBigThing was called:
Water
✓ successfully falls
[myFunction] was called
[doBigThing] was called
Instead I would like to see
Water
✓ successfully falls
[myFunction] stub was called
I am probably missing out on something and I would appreciate your help.
You're stubbing functions object's method myFunction, but in waterfall method you're calling a myFunction function (I actually can't run your code in my environment, I get "ReferenceError: myFunction is not defined"). So this should work:
// functions.js
var functions = {
// function I don't want to run
doBigThing: function() {
console.log("[doBigThing] was called");
},
// function I want to stub
myFunction: function(number, callback) {
console.log("[myFunction] was called");
functions.doBigThing(); // CHANGE HERE
callback(null, number);
},
// function I want to test
waterfall: function(callback) {
return async.waterfall([
async.constant(5), // 5 just for the demo
functions.myFunction // CHANGE HERE
], callback);
}
};
module.exports = functions;

Using callback function with prototype functions

I am having trouble figuring out how to pass the objects method rather than sort "generic prototype" method when doing callback.
function Client() {
this.name = "hello";
}
Client.prototype.apiCall = function(method, params, callback) {
callback();
}
Client.prototype.onLogin = function(error, data) {
console.log(this.name);// undefined!!!!
}
Client.prototype.start = function() {
var self = this;
self.apiCall('rtm.start', {
}, self.onLogin) // passing of method like this does not work.
}
I am passing the onLogin method but well it does not work. This is code I have re-written. Previously I nested all methods inside the Client function but well, I learned that that is not the way to do it so now I am trying using prototype.
I know there is some solution "binding" the onLogin function inside the Client() function but well I want to understand the issue.
You need to bind the apiCalls context to the callback using bind:
Client.prototype.apiCall = function(method, params, callback) {
var bound = callback.bind(this);
bound();
}
Otherwise, the this within onLogin is set to the global object.
See Call, Apply And Bind for further details.
Basically .bind(obj) returns a function which, when called, will internally use (obj) as this.
So you create this bound and then you call it.
You can use call or apply to bind this, see snippet. I've modified your code for demonstration purposes. Hope it clarifies things for you
function Client() {
this.name = "hello";
}
Client.prototype = {
apiCall: function(method, params, callback) {
try {
var trial = method.call(this, params);
callback.apply(this, [null, trial]);
} catch (e) {
callback.apply(this, [e, null]);
}
},
onLogin: function(error, data) {
if (error) {
Helpers.report('<b style="color: red">' +
'An error occured!</b> <i>' +
error.message + '</i>')
} else {
Helpers.report(this.name, ' (data.result = ' + data.result + ')');
}
},
start: function() {
Helpers.useCSS(1);
// error from this.rtm.start
Helpers.report('Command: <code>', 'this.apiCall(this.rtm.start, {will: \'not work\'}, this.onLogin);','</code>');
this.apiCall(this.rtm.start, {will: 'not work'}, this.onLogin);
// this.rtm.works is ok
Helpers.report('<br>Command: <code>',
'this.apiCall(this.rtm.works, {will: \'work\'}, this.onLogin);',
'</code>');
this.apiCall(this.rtm.works, {
will: 'work'
}, this.onLogin);
},
// --------------------------------
// added rtm for snippet demo
rtm: {
start: function(params) {
return anerror;
},
works: function(params) {
return {
result: 'worked, <code>params: ' + JSON.stringify(params) + '</code>'
};
}
},
};
new Client().start(); //<= here
<script src="https://rawgit.com/KooiInc/Helpers/master/Helpers.js"></script>

Javascript anonymous function question

I have this js class which has an anonymous function to retrieve some query results. Since the function which handles the results is anonymous I can't save the results in a this.var variable and use them somewhere else since this in the anonymous function references to the window object. I can't return it as a function return either so how do I do with those results to have them available somewhere else?
someObject = {
// this.db is created, no need to paste that code
dbGetAnimals: function () {
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
return results;
} )
});
},
printAllAnimals: function () {
var animals = this.dbGetAnimals();
alert (animals);// undefined
}
}
someObject.printAllAnimals();
You can create a local variable to hold your this reference. Your anonymous function will become a closure, so it will be able to see that local var.
dbGetAnimals: function () {
var myself = this;
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
myself.var = results;
} )
});
},
You're trying to do traditional synchronous/non-blocking programming where you want to do asynchronous programming.
var someObject = function()({
this.dbGetAnimals = function (callback) {
db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
callback(results);
} )
});
},
this.printAllAnimals = function (callback) {
this.dbGetAnimals(callback);
}
})();
someObject.printAllAnimals(function(animals) {
alert(animals);
}
There are much cleaner ways to do this, but for async programming you have to learn to do everything on callbacks instead of direct returns.
This is happening because the anonymous function in executeSql is a callback function which is not executed until the query compmetes, which by definition will be after dbGetAnimals returns. That's why your call to dbGetAnimals is returning undefined.
You'll have to pass a callback function to receive the query resutls from within the executSql callback:
someObject = {
// this.db is created, no need to paste that code
dbGetAnimals: function (callback) {
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
callback(results);
} );
});
},
printAllAnimals: function () {
this.dbGetAnimals(function(animals) {
alert(animals)
});
}
}
someObject.printAllAnimals();

Categories

Resources