JS: Rebound "this" in contextless function call - javascript

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();

Related

How to retrieve value from process.on in nodejs?

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
});

initializing a nodejs global variable within a function and having it equate the same calling it outside

so I am having some issues with scopes when it comes to nodejs. I am wondering how you can initialize a variable in the global space, initialize it in a function (private scope) then have it equal to what you initialized it to in the private scope when you call it anywhere in the class. One thing I have noticed is that although you can use the variable in a private scope, when it is called again outside that function it just turns undefined. How can I return the same functions initialized when it was private? This is not my issued code but an example in case the picture isn't clear
let name;
class blah {
static blablah() {
name = josh;
}
console.log(name);
//this will return undefined and not josh
}
What I need in my context:
let genesis;
let jsonChain;
class Blockchain {
constructor() {
//this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
//down the file we find the issued function...
static getGenesis() {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if (err) throw err;
jsonChain = JSON.parse(data);
genesis = jsonChain.blocks[0].GENESIS_DATA;
return genesis;
});
//returning here instead inside the callback also yields undefined
//I want to be able to access my contents from json file through
//the genesis variable which is global when I return this function
//but I cannot reach the contents of the callback
}
}
SOLUTION: Make sure to return the promise so that the async function runs later in your program when you call it.
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis().then(function(genesisData) {
console.log(genesisData); // Genesis block data is here.
}, function(err) {
// This only runs if there was an error.
console.log(err);
}));
}
//down the file we find the solved function...
static getGenesis() {
return new Promise(function(resolve, reject) {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if(err) return reject(err);
const jsonChain = JSON.parse(data);
resolve(jsonChain.blocks[0].GENESIS_DATA);
});
});
}
}
module.exports = Blockchain;
Now that you've added the real code, there are a number of issues illustrated there.
if (err) throw err inside a plain asynchronous callback does nothing useful. Nobody can catch that error and thus this should never be used. You need real error handling there. IMO, one should never write that line of code in that circumstance.
return genesis; does nothing useful. That just returns back from the fs.readFile() callback where the internals of fs.readFile() are ignoring any return value. That does not return from the higher level function.
jsonChain = JSON.parse(data); does successfully assign to the higher scoped variable, but any code that is trying to use the jsonChain variable will have no idea when it was assigned a value because fs.readFile() is asynchronous and calls its callback at some future, indeterminate time. So, if you're seeing that jsonChain has no value, that's because you're looking at the variable BEFORE it gets assigned. Basically, this is an anti-pattern. You can't program with asynchronous code that way in node.js. Instead, you need to USE the asynchronous value inside the callback or you need to call some function from within the callback and pass it the value. ONLY in that circumstance do you know WHEN the value is available.
If the top level problem here is that you're trying to return some value you retrieved asynchronously, then you cannot directly do that in Javascript. You can read all about that here where it explains that you have to communicate that asynchronous value back to the caller with a callback, a promise or you can also use an event.
The first problem with your code is that josh is not in quotes. josh is undefined; did you mean 'josh' (in quotes)?
One way is to make your getGenesis() function async, so that you can wait for it to finish assigning the correct value to genesis. This requires wrapping everything inside an anonymous async function:
(async () => {
const fs = require('fs');
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
static async getGenesis() {
jsonChain = JSON.parse(await fs.promises.readFile(jsonRoute, 'utf-8'));
genesis = jsonChain.blocks[0].GENESIS_DATA;
}
}
console.log(genesis); // undefined D:
await Blockchain.getGenesis();
console.log(genesis); // not undefined :D
})();

Why does VS Code step into Node.js internal code when using step over

Debugging in this example code, and pressing f10(step over) each time, debugger will enter into a file name next_tick.js which is a file related to Node.js. Why? I do not want to step into internal code related to Node.js.
Below is example code
function abc() {
return new Promise((resolve, reject) => {
setTimeout(function () { resolve(10) }, 1000)
});
}
async function b(){
const a=10+ await abc()
console.log(a)
}
b()
Below is part of next_tick.js
// Run callbacks that have no domain.
// Using domains will cause this to be overridden.
function _tickCallback() {
do {
while (tickInfo[kIndex] < tickInfo[kLength]) {
const tock = nextTickQueue[tickInfo[kIndex]++];
const callback = tock.callback;
const args = tock.args;
// CHECK(Number.isSafeInteger(tock[async_id_symbol]))
// CHECK(tock[async_id_symbol] > 0)
// CHECK(Number.isSafeInteger(tock[trigger_id_symbol]))
// CHECK(tock[trigger_id_symbol] > 0)
nextTickEmitBefore(tock[async_id_symbol], tock[trigger_id_symbol]);
// emitDestroy() places the async_id_symbol into an asynchronous queue
// that calls the destroy callback in the future. It's called before
// calling tock.callback so destroy will be called even if the callback
// throws an exception that is handles by 'uncaughtException' or a
// domain.
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
// that nextTick() doesn't allow the event loop to proceed, but if
// any async hooks are enabled during the callback's execution then
// this tock's after hook will be called, but not its destroy hook.
if (async_hook_fields[kDestroy] > 0)
emitDestroy(tock[async_id_symbol]);
// Using separate callback execution functions allows direct
// callback invocation with small numbers of arguments to avoid the
// performance hit associated with using `fn.apply()`
_combinedTickCallback(args, callback);
nextTickEmitAfter(tock[async_id_symbol]);
if (kMaxCallbacksPerLoop < tickInfo[kIndex])
tickDone();
}
tickDone();
_runMicrotasks();
emitPendingUnhandledRejections();
} while (tickInfo[kLength] !== 0);
}

How to use object methods as callbacks to modify object properties in javascript

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.

Javascript node.js callback function variable scope problems [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I am in the process of relearning Javascript and last week when writing this code for a university assignment I think that there is probably a much better way of executing this code
app.get('/member/all', function(req, res) {
connection.query('CALL GetAllMembers()', function(err,rows){
connection.query('CALL CountMembers()', function(err, allMembers){
console.log(err);
connection.query('CALL CountAllIndMembers()', function(err,indMembers){
console.log(err);
connection.query('CALL CountInactiveMembers()', function(err,inactiveMembers){
console.log(err);
connection.query('CALL CountAllMembersInGroups()', function(err,groupMembers){
console.log(err);
res.render('members', {members : rows[0], title : "All Members", groupMembers : groupMembers[0][0].AllGrpMembers,
inactiveMembers : inactiveMembers[0][0].AllInactiveMembers, indMembers : indMembers[0][0].AllIndMembers,
allMembers : allMembers[0][0].AllMembers, statistics : true});
});
});
});
});
});
});
});
When I was trying to declare variables under the app.get such as var allMembers... when the callback was executed I was unable to set allMembers = rowsFromTheCallback. It seemed that it was a local variable to that callback. I'm sure this is something to do with the variable scope and/or hoisting. Just wanted to ask you guys if there would be a better way to do this as even though this function works. It is very ugly to look at haha!
Thanks in advance
Jack
As far as scope goes, all the inner functions should be able to read and write to the outer variable unless it is shadowed by an inner variable declaration or function parameter.
The problem you are having might be related to the async-ness of the code. See this code:
function delay(n, cb){
setTimeout(function(){ bs(delay) }, delay);
}
function main(){
var allMembers = 17;
delay(500, function(){
console.log(allMembers); // This looks at the outer "allMembers"
allMembers = 18;
delay(200, function(allMembers){ // <-- SHADOW
console.log(allMembers); // This looks at the allMembers from "delay 200"'s callback
allMembers = 42;
});
delay(300, function(){
console.log(allMembers); //This is the outside "allMembers" again
});
});
return allMembers; // Still 17!
}
main();
main will return before the setTimeouts have even fired so its going to return the original value of that variable. In order to wait for the inner callbacks to run, the only way is to make main take a callback to signa when its done, instead of just returning.
function main(onResult){
delay(500, function(){
//...
onResult(allMembers);
});
// <-- no return value
});
main(function(allM){
console.log(allM);
});
See async library: https://github.com/caolan/async
async.series([
getAllMembers,
countMembers,
...
], function(err, results) {
// err contains an error if any of the functions fails. No more functions will be run.
// results is an array containing results of each function if all the functions executed without errors
}));
function getAllMembers(callback) {
connection.query('CALL CountMembers()', callback);
}
function countMembers(callback) {
...
}
If the execution order of the functions does not matter, async.parallel can be used instead of async.series.
There is power in using a library to handle and encapsulate "Continuation Passing Style" (CPS) interactions with your asynchronous calls. The following code isn't from a library, but I'm going to walk through it and use it as an example of one way to implement CPS.
Setting up a scope appropriate queue is the first step. This example uses about the most simple method for doing so:
var nextList = [];
After that we need a method to handle our first case, the need to queue tasks to be performed in the future. In this case I was focused on performing them in order so I named it next.
function next() {
var todo,
current,
task,
args = {};
if (arguments.length > 0) { // if called with parameters process them
// if parameters aren't in an array wrap them
if (!Array.isArray(arguments['0'])) {
todo = [arguments];
} else { // we were passed an array
todo = [];
arguments['0'].forEach(function (item) {
// for each item we were passed add it to todo
todo.push(item);
});
}
nextList = todo.concat(nextList);
// append the new items to the end of our list
}
if (nextList.length > 0) { // if there are still things to do
current = Array.prototype.slice.apply(nextList.shift());
task = current[0];
args = current.slice(1);
task.apply(null, args); // execute the next item in the list
}
}
This allows us to make calls like:
.map(function (filepath) {
tasks.push(
[
handleAsset,
{
'path': filepath,
}
]
);
});
tasks.push([done]);
next(tasks);
This will call handleAsset, which is async, once for each file, in order. This will allows you to take your code and change each of the nested calls into a separate function in the form:
function memberAll() {
app.get('/member/all', function(req, res) {
if (err) {
handleError(err, 'memberAll');
} else {
next(getAllMembers, 'parameters to that call if needed');
}
});
}
where handleError is a common error handler, and the next call allows you to pass on relevant parameters to the next function that is needed. Importantly in the success side of the if statement you could either:
conditionally call one of several functions
call next with an array of calls to make, for instance if you had functions for processFolder and processFile you could expect that processing a folder might involve processing other folders and files and that the number would vary
do nothing except call next() with no parameters and end the current branch
Embellishments can include writing a clean function for emptying the nextList, adding items to nextList without calling an item from the list, etc. The alternative at this point is to either use an existing library for this or to continue writing your own.

Categories