The current code that I am using:
async function MainFunction(){
let arrOO;
process.on('message', (jData) => {
let sName;
if(jData.koota){
sName = jData.koota[0]
}
console.log(sName + ' hello!')
arrOO = sName
})
console.log(arrOO + ' calling outside of .on')
}
I am attempting to print jData.koota[0] which is assigned to sName so that it can used in the whole function. In this case, outside of the process.on, I want to print it. When this code is run, 'undefined' is returned at console.log(arrOO + ' calling outside of .on'). How would I call sName outside of process.on?
process.on() just installs an event listener and immediately returns, so your console.log() runs BEFORE any events have happened. process.on() is referred to as non-blocking. That means it does some initial work and then immediately returns and the actual data arrives sometime later when it calls your callback.
So, your console.log() is just attempting to look at the data before any data has been put in there. Note that most of the time when you're trying to assign to a higher scoped variable from an asynchronous callback function, that's a likely sign of a coding error because code at the higher scope won't have any idea when to access that data.
For any code that wants to know about those process messages, you need to either put that code inside the process.on() callback or put it in a function that you call from inside that callback. That's the ONLY way you will know when an incoming message has occurredl and when some data is available.
Here's one option:
function MainFunction(){
process.on('message', (jData) => {
let sName;
if(jData.koota){
sName = jData.koota[0];
// call some function and pass it the new data
newDataArrived(sName)
}
});
}
// this function gets called when we have a new sName
function newDataArrived(sName) {
// put code here that uses the data
console.log(sName);
}
Or, you can make it a promise based interface that resolves a promise on the next matching event you get:
function MainFunction() {
return new Promise(resolve => {
function messageHandler(jData) {
if (jData.koota) {
resolve(jData.koota);
// remove message listener so they don't pile up
// since a promise is a one-shot device, we can
// only use this promise once
process.off('message', messageHandler);
}
}
process.on('message', messageHandler);
});
}
Mainfunction().then(sName => {
// use sName here
});
Related
The function doSomethingElse in this example fails to execute since its this has been rebound to window or global (if in Node) due to a contextless call inside app.populateDatabase.
Is there any way to avoid this without referencing app inside every function?
loadDatabase function executes a callback according to a logic statement, if an imaginary database didn't exist, it populates it after loading, then the populateDatabase executes the callback it has been provided.
I cannot rebind the onLoaded argument to app since I don't know where it comes from, and the bind/apply/call abstraction overuse creates quite a mess.
var app = {};
app.loadDatabase = function(onLoaded) {
// If database already exists, only run a callback
var callback = onLoaded;
// If database doesn't exists, populate it, then run a callback.
if (!databaseExists) {
callback = this.populateDatabase.bind(this, onLoaded);
}
this.database = new sqlite.Database("file.db", function(error) {
if (error) { ... }
callback();
})
}
app.populateDatabase = function(onPopulated) {
// Contextless call here. <--------
onPopulated();
}
app.doSomethingElse = function() {
// this != app due to contextless call.
this.somethingElse();
}
app.run = function() {
// Load the database, then do something else.
this.loadDatabase(this.doSomethingElse);
}
app.run();
Just replace this.loadDatabase(this.doSomethingElse);
with this.loadDatabase(() => this.doSomethingElse());. This way you create a new arrow function but then doSomethingElse is called with the right this context.
You could also do .bind but I recommend the arrow function. Here with bind: this.loadDatabase(this.doSomethingElse.bind(this))
In general consider to move to promises & maybe async functions. Then do this:
this.loadDatabase().then(() => this.doSomethingElse());
or better with an async function:
await this.loadDatabase();
this.doSomethingElse();
I'm following along with the Kyle Simpson Rethinking Asynchronous JavaScript video course and am confused about how his thunk pattern is using closures. The code is like this:
function ajax(url, cb) {
$.ajax({ url: `text/${url}`, type: 'GET', dataType: 'json' })
.done(data => {
cb(data)
})
}
function getFile(file) {
var text, fn;
ajax(file,function(response){
if (fn) {
fn(response)
} else {
text = response
}
})
return function th(cb) {
if (text) {
cb(text)
} else {
fn = cb
}
}
}
var th1 = getFile('1')
var th2 = getFile('2')
var th3 = getFile('3')
th1(function ready(text){
console.log(text)
th2(function ready(text){
console.log(text)
th3(function ready(text){
console.log(text)
th2(function (text) {
console.log(text)
})
})
})
})
I added the extra call to th2 in the final nested portion. I expected this use of closures to return the value originally printed from th2, stored in the closure variable text in the getFile function, ie not make another network call. Though that is not what happens: execution stops after printing the text in the t3 callback.
Why doesn't this closure return its already retrieved value?
I expected this use of closures to return the value originally printed from th2, stored in the closure variable text in the getFile function. Though that is not what happens: execution stops after printing the text in the t3 callback.
The problem with these thunks is that you cannot use them twice (at least, when the first use is asynchronous). The value is never stored in the closure variable text. Why? Because th2 was run the first time before the ajax call succeeded, which ran
if (text) // still empty
…
else // nope, nothing yet, store for later
fn = cb
Then later, when ajax calls the callback, it will only run
if (fn) // we got something to call
fn(response)
…
and not text = response. So when th2 is called a second time later (or, worse, immediately from within the callback), it will again just try to store the cb in the fn variable, but nothing will call that.
A possible workaround would be to do
… // in the thunk closure
ajax(file, function(response) {
text = response;
if (fn) {
fn(response)
}
})
instead which would make your code work, but still be broken: what if th2 is called multiple times before the asynchronous ajax callback? Then cb overwrites the previous fn, so ultimately we would need to maintain an array of callbacks that all should get called. Well, do that and add chainability for the callbacks, and you've got the most basic Promise implementation already.
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
};
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.
EDIT
Let me get more to the point. I'm trying to create a psuedo promise implementation. The idea here being that I have a callback that won't be executed until an asynchronous call is received. So I'm simply queueing up all the calls to this function until the time at which it's notified that it can be executed. The queue is emptied and any further call to the function is SUPPOSED to execute immediately, but for some reason, the function is still queueing. This is because, for whatever reason, my redefinition of the runner function is not working correctly. The code below was my sleep deprived, frustrated version of every thought that went through my head. Here's the actual code:
function Promise(callback){
var queue = []
, callback = callback
, runner = function(){
queue.push({
context: this,
args: Array.prototype.slice.call(arguments, 0)
});
}
;//var
runner.exec = function(){
for(var i = 0, ilen = queue.length; i < ilen; i++){
var q = queue[i];
callback.apply(q.context, q.args);
}
runner = callback;
};
return runner;
}
test = Promise(function(){
$('<div/>').appendTo('#output').html(Array.prototype.slice.call(arguments,0).toString());
});
test(1,2);
test(3,4);
test.exec();
test(5,6);
http://jsfiddle.net/a7gaR/
I'm banging my head against the wall with this one. I'm trying to reassign variables in a function from a call outside the function itself (ideally by passing a reassignment function as a callback). In the example I posted on jsfiddle, I made a global function that, in theory, has a reference to the variables contained within its parent function. Upon calling that external function, I expect it to reassign the values that the other function is using. It doesn't seem to work this way.
window.test = function temp() {
var val = 7,
func = function() {
return val;
};
window.change = function() {
window.test.val = 555555;
$('<div>Changing ' + val + ' to ' + window.test.val +
'</div>').appendTo($output);
val = window.test.val;
temp.val = window.test.val;
func = function() {
return 'Why isn\'t this working?';
}
}
return func();
}
var $output = $('#output');
$('<div/>').appendTo($output).html('::' + test() + '::');
window.change();
$('<div/>').appendTo($output).html('::' + test() + '::');
http://jsfiddle.net/YhyMK/
The second time you call test you're creating a new local variable called func and defining a new window.change that closes over that new variable. The changes you made to the original func by calling the original window.change are not relevant in the second call.
Also note that the following line:
window.test.val = 555555;
...does not modify/refer to the val variable in the outer function. window.test.val refers to a property named val on the test object (which happens to be a function), not any local variable.
You are trying to refer to a local variable in a function with the syntax func.varname. That won't work, that's not the way local variables work.
I finally created a function that would perform this operation. The gist for it is here: https://gist.github.com/2586972.
It works like this...
You make a call to Defer passing the callback whose functionality you'd like to delay:
var deferredCB = Defer(function(){ console.log(this,arguments) };
deferredCB will now store all of the arguments you pass allowing them to be executed at some later date:
defferedCB(1);
defferedCB(2);
Now, when you're ready to perform the operation, you simply "execute" deferredCB:
defferedCB.exec();
Which results in:
// window, 1
// window, 2
All future calls to deferredCB will be executed immediately. And now that I'm thinking about it, I'll probably do a rewrite to allow you to reset deferredCB to it's pre-executed state (storing all the arguments again).
The key to making it work was having a wrapper function. Javascript simply won't let you reassign a function while it's being executed.
TADA!!!
I'm attempting to model the data in my database as "classes" in server-side JavaScript (Node).
For the moment, I'm simply using the traditional constructor pattern with prototype methods where appropriate and exposing the constructor function as the entire module.exports. The fact that this is server side is unimportant to the core of the question, but I figured I'd provide it as reference.
The problem area of code looks like this:
User.prototype.populate = function() {
var that = this;
db.collection("Users").findOne({email : that.email}, function(err, doc){
if(!err) {
that.firstName = doc.firstName;
that.lastName = doc.lastName;
that.password = doc.password;
}
console.log(that); //expected result
});
console.log(that); //maintains initial values
};
Whenever I call this function, changes to the object do not persist once the findOne() has completed. I realize that the scope of this changes to the global object with new function scopes, so I maintained its reference as that. If console.log(that) from within the anonymous function, the data shows up in the properties of it as expected. However, if I log that once the function has finished, it maintains the state it had at the beginning of the function.
What's going on here and how I can I change instance variables as expected?
"However, if I log that once the function has finished, ..."
By this I assume you're doing something like this...
var user = new User
user.populate();
console.log(user);
If so, the console.log will run long before the asynchronous callback to .findOne() has been invoked.
Any code that relies on the response to findOne needs to be invoked inside the callback.
EDIT: Your update is a little different from my example above, but the reason is the same.
The entire reason for passing a callback to the findOne method is that it performs an asynchronous activity. If it didn't, there's be no reason for the callback. You'd just place the inner code directly after the call to findOne, as you did with the console.log().
But because it's asynchronous, the subsequent code doesn't wait to execute. That's why you're getting the unpopulated object in the console.
If you add a label to each console.log(), you'll see that they execute out of order.
var that = this;
db.collection("Users").findOne({email : that.email}, function(err, doc){
if(!err) {
that.firstName = doc.firstName;
that.lastName = doc.lastName;
that.password = doc.password;
}
console.log("inside the callback", that); // this happens Last!!!
});
console.log("outside the callback", that); // this happens First!!!
So it becomes clear once you observer the order of the console.log calls that the empty one is happening before the one inside the callback.
EDIT: You can also have your .populate() method receive a callback that is invoked inside the .findOne callback.
User.prototype.createNickName = function () {
this.nickname = this.firstName.slice(0,3) + '_' + this.lastName.slice(0,3);
};
// >>>------------------------------v----receive a function argument...
User.prototype.populate = function(callback_func) {
var that = this;
db.collection("Users").findOne({email : that.email}, function(err, doc){
if(!err) {
that.firstName = doc.firstName;
that.lastName = doc.lastName;
that.password = doc.password;
}
// all users will have the "createNickName()" method invoked
that.createNickName();
// ...and invoke it in the callback.
callback_func.call(that);
// By using .call(), I'm setting the "this" value
// of callback_func to whatever is passed as the first argument
});
};
// this user wants to log the firstName property in all caps
var user1 = new User;
user1.populate(function() {
console.log(this.firstName.toUpperCase());
});
// this user wants to log the the whole name
var user2 = new User;
user2.populate(function() {
console.log(this.firstName + ' ' + this.lastName);
});
// this user wants to verify that the password is sufficiently secure
var user3 = new User;
user3.populate(function() {
console.log(verify_password(this.password));
});