Reputation: 291
I am building an interface to a REST API, and in the process of using the javascript request module, I found trouble trying to return values from the callback. Request does not work that way, and you must handle the data from within the callback.
But I need to process and compare data from many requests, so I decided to push the data to some database object from within my callback.
I made an prototype function to be called as the callback to keep the data structure modular.
I am baffled because when I try to modify this.value from within my callback function, the result does not go to the proper place.
I expect the callback function to modify my instance of the database, and to be able to access that change later on after waiting for the callback to finish.
In the code sample below, I show that I am I am able to do exactly this with globalString, but in globalDatabase, the assignment does not survive past the end of the callback function.
I suspect I may be using my object pointers incorrectly. can anyone point out the flaw in how I modify this.value, or provide a good alternative to the way I am using OOP here.
A good solution should be able to assign a value from inside the callback and then access that value from another function which is not called by the callback.
What is the best way to store the data from my callback?
var Database = function(){
console.log("<Executing constructor>");
this.value = "initial constructor data";
};
Database.prototype.myCallback = function(error, response, body){
console.log("<Executing callback>");
this.value = body;
globalString = body;
};
globalString = "blank";
globalDatabase = new Database();
console.log(globalString, "|" ,globalDatabase.value);
main();
function main(){
var request = require('request');
requestParams = {
url: "http://ip.jsontest.com/",
method: "GET",
json: true
};
request(requestParams, globalDatabase.myCallback);
console.log(globalString, "|" ,globalDatabase.value);
setTimeout(function() {
console.log(globalString, "|" ,globalDatabase.value);
}, 2 * 1000);//seconds wait time for callback to finish
};
I was able to replicate this problem using callbacks in setTimeout.
var Database = function(){
console.log("<Executing constructor>");
this.value = "initial constructor data";
};
Database.prototype.myCallback = function(){
console.log("<Executing callback>");
this.value = "callback modified data";
};
d = new Database();//global target for async modification
main();
function main(){
console.log("First, the object contains: ",d.value);
setTimeout(d.myCallback, 1 * 1000);//seconds wait time
console.log("Back in main, the object contains: ", d.value);
setTimeout(function() {
console.log("After waiting patiently, the object contains: ",d.value);
}, 2 * 1000);//seconds wait time
};
Upvotes: 0
Views: 2574
Reputation: 15770
This is a well-known "quirk" of Javascript: when you call your myCallback
function from the request
function (or from setTimeout
), the context of the call is the request
function - this means that this
refers to request
, not to your Database
object. So, for example, if you call myCallback
from a DOM event handler, then this
will refer to the DOM element.
There are a number of good answers explaining this: here or here.
Now, for a solution to your specific problem, here's a code sample. I took the liberty of rewriting your second example using ES6 classes, since I think it's a little clearer that way:
class Database {
constructor() {
console.log('<Executing constructor>');
this.value = 'initial constructor data';
// bind `this` to the `myCallback` function so that whenever we call
// `myCallback`, it will always have the correct `this`
//
this.myCallback = this.myCallback.bind(this);
}
myCallback() {
console.log('<Executing callback>');
this.value = 'callback modified data';
}
}
let d = new Database(); //global target for async modification
main();
function main(){
console.log("First, the object contains: ",d.value);
setTimeout(d.myCallback, 1 * 1000);//seconds wait time
console.log("Back in main, the object contains: ", d.value);
setTimeout(function() {
console.log("After waiting patiently, the object contains: ",d.value);
}, 2 * 1000);//seconds wait time
};
Notice the call the bind
in the constructor. That "replaces" the myCallback
method with a new version of the same method where the context is always the this
that refers to the class.
Upvotes: 3