Module.exports function executes before Console log - why? - javascript

I have read a few answers to similar questions, but haven't been able to understand why my application behaves this way.
I am updating a CLI application to be modular. The main script imports a function called questionOne after a console.log:
const questionOne = require('./modules/questionOne');
console.log('\n');
console.log('Which DB do you want to use?'.inverse);
questionOne();
questionOne uses readline-sync to ask a question and execute code depending on the answer. But, when I run my application... the question is asked first, and then Which DB do you want to use runs after the user has asked the question. Why?
For Reference
The code for questionOne:
const colors = require('colors');
const readlineSync = require('readline-sync');
//Data Modules
const { dbList } = require('../data/dbList.json');
const db = readlineSync.keyInSelect(dbList);
//people = 0 || offers = 1 || networks = 2
const questionOne = () => {
if (db === 0) {
console.log('\n');
console.log('Which Collection would you like to use?'.inverse.bold);
// const colRef = readlineSync.keyInSelect();
// dbPeople(colRef);
} else if (db === 1) {
console.log('You picked offers');
} else if (db === 2) {
console.log('You picked networks');
} else {
process.exit();
}
};
module.exports = questionOne;
I understand that I can put the console.log inside the module. But I am curious why javascript behaves this way?

When you first to require('./modules/questionOne');, that runs all the code at the top level of the module you are loading. And, it runs that code synchronously, meaning it doesn't return until that top level code returns.
So, in your example, that's going to run all the code at the top level of your questionOne module. That top level code includes this line:
const db = readlineSync.keyInSelect(dbList);
So, that line of code will run before the module has finished loading and before it returns from this:
const questionOne = require('./modules/questionOne');
So, that should explain why the first thing that happens is that you get the prompt from const db = readlineSync.keyInSelect(dbList);. If you want to display something before that, then put that before it in the code.
Then, after that promise is done, the only other things that happen in your questionOne module are the definition of the questionOne function and the assignment module.exports = questionOne;. At that point, the require('./modules/questionOne'); returns and the value from the module.exports is assigned to your questionOne variable in your main module.
Then, and only then, the rest of your main module runs and it executes:
console.log('\n');
console.log('Which DB do you want to use?'.inverse);
questionOne();
I understand that I can put the console.log inside the module. But I am curious why javascript behaves this way?
It's just executing code in the order encountered. Nothing here appears asynchronous so it's just simple run one line of code after the other as they are encountered.

Related

Discord bot Command That changes Value of a different command

I'm looking for a way to change a text string or a value in one command by typing the new value in a different command. For example I have Discord js v12 and I'm using module commands with each command being in its own .js file.
module.exports = {
name: 'calc',
cooldown: 1000,
run: async(client, message, args) => {
if (!message.member.hasPermission("ADMINISTRATOR")) return await message.delete();
await message.delete();
var multiply = args[0] * (100 - percalc) / 100;
var calculation = multiply.toFixed(2);
if(!args[0]) return await message.channel.send('Specify a Value');
await message.channel.send(changableValue);
await message.channel.send(calculation < 5 ? 5 : calculation);
}
and I have the consts in the config file like
const percalc = 50;
const changableValue = 'Text example';
Right now the command _calc {number} puts out a calculation in percentage based on the percalc const and a text that comes with it in the changableValue part.
I'd like to make a command let's say _calcset {Value} that will save the provided value and will send it in place of the changableValue const.
First of all, note that the only reason the keyword const exists is because it stands for constant and constant variables can never be changed once initialized. So make sure you change the variable declaration keyword to just var or let.
Method 1 - If you don't need data to persist
Now, if you only want the variable to be changed per session, and you're fine with it going back to what you defined it as when you shut down the bot, you can just update the variable using functions exported from the js file. But to get the dynamic variable you'll also need to use a getter function that you export as well. Example:
config.js
var changeableValue = "foo";
function getValue() {
return changeableValue;
}
function updateValue(newValue) {
changeableValue = newValue;
}
module.exports = {
getValue,
updateValue
}
command.js
const { getValue, updateValue } = require("config.js");
console.log(getValue()); // logs "foo"
updateValue("bar");
console.log(getValue()); // logs "bar"
Unfortunately, as I mentioned, the changeableValue var will be reset back to "foo" every time you shut off the bot. If that's okay with you, then the above works fine.
Method 2 - If you need data to persist through sessions
If you want to persist the changeableValue variable through sessions, then it gets a little more complicated. Your two most likely options are either to write the value to a JSON file using the fs module (so it will be saved to your disk), or save the value in some other database, like MongoDB. I would recommend using another database provider because there are more problems you can run into when writing to your own disk, for example, if you make two write requests at the same time (like if two users use the command at the same time), you can corrupt the file when the requests try to write at the same time. However setting up an external database is outside of the scope of this question, so here's how you would set up writing to a JSON file:
config.json
{
"changeableValue": "foo"
}
command.js
const fs = require("fs");
var { changeableValue } = require("config.json");
console.log(changeableValue) // logs "foo"
var updatedValueJSON = JSON.stringify({ changeableValue: "bar" }); // necessary because the JSON object must be converted to a string
fs.writeFile("config.json", updatedValueJSON, "utf8", () => {
// this is the callback function called when the file write completes
let { changeableValue } = require("config.json");
console.log(changeableValue); // logs "bar", and now if you restart the bot and import changeableValue, it will still be bar
});

What's the advantage of fastify-plugin over a normal function call?

This answer to a similar question does a great job at explaining how fastify-plugin works and what it does. After reading the explanation, I still have a question remaining; how is this different from a normal function call instead of using the .register() method?
To clarify with an example, how are the two approaches below different from each other:
const app = fastify();
// Register a fastify-plugin that decorates app
const myPlugin = fp((app: FastifyInstance) => {
app.decorate('example', 10);
});
app.register(myPlugin);
// Just decorate the app directly
const decorateApp = (app: FastifyInstance) => {
app.decorate('example', 10);
};
decorateApp(app);
By writing a decorateApp function you are creating your own "API" to load your application.
That said, the first burden you will face soon is sync or async:
decorateApp is a sync function
decorateAppAsync within an async function
For example, you need to preload something from the database before you can start your application.
const decorateApp = (app) => {
app.register(require('#fastify/mongodb'))
};
const businessLogic = async (app) => {
const data = await app.mongo.db.collection('data').find({}).toArray()
}
decorateApp(app)
businessLogic(app) // whoops: it is async
In this example you need to change a lot of code:
the decorateApp function must be async
the mongodb registration must be awaited
the main code that loads the application must be async
Instead, by using the fastify's approach, you need to update only the plugin that loads the database:
const applicationConfigPlugin = fp(
+ async function (fastify) {
- function (fastify, opts, next) {
- app.register(require('#fastify/mongodb'))
- next()
+ await app.register(require('#fastify/mongodb'))
}
)
PS: note that fastify-plugin example code misses the next callback since it is a sync function.
The next bad pattern will be high hidden coupling between functions.
Every application needs a config. Usually, the fastify instance is decorated with it.
So, you will have something like:
decorateAppWithConfig(app);
decorateAppWithSomethingElse(app);
Now, decorateAppWithSomethingElse will need to know that it is loaded after decorateAppWithConfig.
Instead, by using the fastify-plugin, you can write:
const applicationConfigPlugin = fp(
async function (fastify) {
fastify.decorate('config', 42);
},
{
name: 'my-app-config',
}
)
const applicationBusinessLogic = fp(
async function (fastify) {
// ...
},
{
name: 'my-app-business-logic',
dependencies: ['my-app-config']
}
)
// note that the WRONG order of the plugins
app.register(applicationBusinessLogic);
app.register(applicationConfigPlugin);
Now, you will get a nice error, instead of a Cannot read properties of undefined when the config decorator is missing:
AssertionError [ERR_ASSERTION]: The dependency 'my-app-config' of plugin 'my-app-business-logic' is not registered
So, basically writing a series of functions that use/decorate the fastify instance is doable but it adds
a new convention to your code that will have to manage the loading of the plugins.
This job is already implemented by fastify and the fastify-plugin adds many validation checks to it.
So, by considering the question's example: there is no difference, but using that approach to a bigger application
will lead to a more complex code:
sync/async loading functions
poor error messages
hidden dependencies instead of explicit ones

Prevent node.js application from exiting once the process is done

I wrote a really basic nodejs application which look like this:
#!/usr/bin/env node
const inquirer = require("inquirer");
const chalk = require("chalk");
const figlet = require("figlet");
const shell = require("shelljs");
const fs = require('fs');
//Defining some functions
//~~
//The main part of the code
const run = async function() {
const answers = await form();
//other codes
//~~
}
run();
The main purpose of the code is to use the inquirer module to ask some question in the console, and then process the answers in the run() part. It does perfect job on that. It successfully asks the question and do what it should do with the answers.
However, The process would exit once the answer have been processed. What I want is once the answer have been processed, I want it to answer the same question again, and keep repeating that until I terminate the process manually.
I tried this:
for( ; ; ) {
run();
}
However, It would then answer questions again and again without waiting for answers. Here is how the console looked like:
console output
I want it to do these:
Ask question and wait untill I enter the answer.
Process the question (with the codes in run())
Once it's done, go back to question 1.
How can I do this?
Instead of doing
for( ; ; ) {
run();
}
Do this inside run function
while (true) {
const answers = await form();
//other codes
//~~
}

objects are not initialized in another .js file?

[SOLVED - see at the end please]
[EDITED]
I have the following source:
index.js:
functions = require('firebase-functions');
admin = require('firebase-admin');
globals = require('./globals');
.....
admin.initializeApp();
exports.gateway = functions.https.onCall((data, context) => {
.....
return globals.dumpItemName(1);
});
exports.POST_gateway = functions.https.onRequest((req, res) => {
.....
return globals.dumpItemName(1);
});
globals.dumpItemName(1);
globals.js:
....
ITEM_NAMES = new Map()
.set(1, 'item1')
.set(2, 'item2');
....
getMapValue = function(map, key) {
let value = map.get(key);
if (value === undefined) {
throw new Error('Missing value for key ' + key);
}
return value;
}
....
dumpItemName = function(key) {
console.log(getMapValue(ITEM_NAMES, key));
}
exports.dumpItemName = dumpItemName;
I'm facing the following problem:
If I run/debug it locally by F5 it executes fine and dumps the expected value.
If I try to deploy it by firebase deploy --only functions
and then call via postman an https.onRequest(...) or https.onCall(....) function which tries to do the same:
globals.dumpItemName(1);
it fails with 'Missing value for key 1'
So I guess when I run it locally with F5 it executes index.js like a program and loads required modules and also run the code in them - so the Map in globals.js is initialized.
But when I deploy it via firebase deploy --functions only it does not execute the code.
So what should I do in order to have Map initialized after deploy ?
[SOLVED - THE REASON FOR THE "PROBLEM"]
Extremely dummy of my side.
Obviously for some of the local tests
(I have by now 10 end-to-end test scenario functions) I've put in index.js the following piece of code:
//!!! JUST FOR TEST - REMOVE IT !!!
ITEM_NAMES = new Map()
.set(globals.ITEM_TEST, 'test');
//!!! JUST FOR TEST - REMOVE IT !!!
so this map was redeclared with just one value in it. and the value of globals.ITEM_TEST is 200.
and it was used in one temporary test scenario function.
And just bad coincidences:
obviously I was too tired and forgot about it.
my other local tests were commented and that's why the local run was successful - in this temporary scenario, only this value was used.
I've decided to deploy in this exact moment. And none of the values I used in my onCall() or onRequest() functions used globals.ITEM_TEST (in real and in dummy both)
So it is...
Sorry for bothering. There is no problem in fact.

JavaScript Autoloader: How To Recover From Errors

I have a proof-of-concept working for a JavaScript autoloader, but it currently suffers from a major flaw: it requires the code to be re-executed in its entirety rather than simply trying again from the line that failed.
Here is the prototype:
<!doctype html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
var app = function(){
console.log('Initialize App');
var test = new Test();
var foo = new Foo();
var bar = new Bar();
};
var autoload = function(app){
var recover = function(error){
var name = error.message.split(' ')[0];
console.log('Loading '+name);
//A file could be synchronously loaded here instead
this[name] = function(){
console.log(name+' has been dynamically created');
};
load(app);
};
var load = function(app){
try {
app();
} catch (error){
if (error.name == "ReferenceError"){
console.log(error.message);
recover(error, app);
}
}
};
load(app);
};
autoload(app);
</script>
</body>
</html>
How It's Supposed To Work
The idea is that all of your application code would get executed within the app function. Eventually, if I can get it working properly, you could also pass in a dependency map to autoloader with the app function to synchronously load dependencies when a function is not defined. The dependency map would simply be an object mapping function names to file names.
How It Currently Works
If you don't feel like trying it out, the above code outputs the following to the console:
Initialize App
Test is not defined
Loading Test
Initialize App
Test has been dynamically created
Foo is not defined
Loading Foo
Initialize App
Test has been dynamically created
Foo has been dynamically created
Bar is not defined
Loading Bar
Initialize App
Test has been dynamically created
Foo has been dynamically created
Bar has been dynamically created
The complete app function is re-executed each time the autoloader catches an error. Obviously, this is less than ideal for a number of reasons.
Recovering From The Error
To move to the next step for making this work, I need to find a way to recover from the error without re-executing the entire app function. The error object from the catch block does provide both the line number and file name where the error occurred, but so far, I haven't been able to find a way to take advantage of that information. There are three general approaches that I can think of:
Restart script execution at the given line
Restart script execution at the beginning, but skip all lines until the given line
Grab the file as a string, split it into an array by line number, and eval the remaining lines.
Unfortunately, I wasn't able to find information on either of the first two approaches. Of the three, #1 seems like it would be more ideal, but I would certainly be open to other creative suggestions as well. As far as I can tell, JavaScript doesn't provide a way to start script execution at an arbitrary line number. #3 might work, but I'm not sure it would be very performant. The only way I can think of doing it would be to require an extra request each time to load the file text into a string.
The Questions
This is admittedly pushing the boundaries of how dependencies could be loaded in JavaScript. I'm not even sure if it is possible because I don't know if JavaScript allows for this type of error recovery. That said, I'm interested in exploring it further until I find out it's absolutely impossible.
In the interests of getting this working:
Is there a way to start script execution at an arbitrary line of JavaScript?
Are there other approaches to this that might be more fruitful? Feel free to be creative!
Taking a step back to look at the bigger picture (assuming I can get this to work):
Is a JavaScript autoloader something that people would even want?
What are the advantages/disadvantages to an autoloader like this vs. an approach like AMD?
What kind of performance issues would be encountered with this approach? Would the performance hit be too much to make it worth it?
It's kind of complicated to make, throwing and catching is kind of expensive as well. You could use typeof window["Test"]!=="function" and then create it instead of using the try catch like this.
But for a general recover and continue approach the following code would do the trick.
var app = (function(i){
var objects=new Array(3),
fnNames=["Test","Foo","Bar"];
return function(){
var len=fnNames.length
,test,foo,bar;
//check if closure vars have been reset, if so
// this has ran successfully once so don't do anything?
if(objects===false){
console.log("nothing to do, initialized already");
return;
}
while(i<len){
try{
objects[i] = new window[fnNames[i]]();
i++;
}catch(e){
if (e.name == "TypeError"){//different syntax different error
throw {"fnName":fnNames[i]};
}
}
}
//store instances in the variables
test = objects[0];
foo = objects[1];
bar = objects[2];
//reset closure vars assuming you only call app once
// when it's successful
i=null;objects=false;
console.log("Got all the instances",test,foo,bar);
};
}(0));
var autoload = function(app){
var recover = function(name){
console.log('Loading '+name);
//A file could be synchronously loaded here instead
this[name] = function(){
this.name=name;
console.log(this.name+' has been dynamically created');
};
load(app);
};
var load = function(app){
try {
app();
} catch (error){
//if statement here no longer needed
// object thrown has fnName (function name)
recover(error.fnName, app);
}
};
load(app);
};
autoload(app);
autoload(app);

Categories

Resources