So im working on a nodejs task that runs update tasks from cli
a snippet of my code
var
npm,
grunt,
async,
path,
which,
color,
isRoot,
progressBar,
EventEmitter,
exec,
utils,
format;
function loadDependencies() {
npm = require('npm');
grunt = require('grunt');
async = require('async');
path = require('path');
which = require('which');
color = require('cli-color');
isRoot = require('is-root');
progressBar = require('progress');
EventEmitter = require("events").EventEmitter;
exec = require('child_process').exec;
utils = require('../utils');
format = require('util').format
}
// Expose my module
module.exports = update;
function update(args, options, callback, ee){
//Load all my deps
loadDeps();
// Create my array
var taskCollection = [];
// Convert the update object with its functions into an array
for(var tasks in update){
taskCollection.push('update.' + tasks);
}
**After playing around I decided to try adding the following array here **
taskCollection = [update.one, update.two];
And it ran the tasks as it should so I can now only assume its how im making my array
//And this is were im stuck *
async.series(taskCollection,
function(err,result){
console.log(error);
console.log(result);
});
}
update.one = function(callback){
callback(null, 'Update One Complete);
}
update.two = function(callback){
callback(null, 'Update Two Complete);
}
Now the idea is that the list of tasks for async to run is automatically populated from my update object, meaning that anything like this
update.bower = function(){}
update.grunt = function(){}
will be run by async but other tasks like
var getVersion = function(){}
Wont the problem comes when I want to run async on the array of tasks Ive created I keep getting the error task() is not a function
Im trying to use
async.series(taskCollection, finish);
and some sort of variation of
async.each(taskCollection);
The ultimate goal will be that a progress bar will be added and so on the callback for each function will call progress.tick() with the number of ticks being automatically counted from the list of tasks
Whats the best way forward?
which async (NodeJs Version) task is best series because i want them in order each as they are an array of functions ?
Thanks in advance banging my head against the wall
Updated!!
The Error is generated from async.js:718 and says
task(_restParam(function (err,args))) {
^
TypeError: task is not a function
I was passing async an array of strings rather than objects.
Consider the following:
console.log(update);
// And you get
{[Function: update] one: [Function], two [Function]}
So in my above code
// Translation - For all the keys in the update object
for(var tasks in update){
// push that key as a string :(
taskCollection.push('update.' + tasks);
}
Update the for to the following:
for (var obj in tasks){
taskCount.push(obj);
taskCollection.push(tasks[obj]);
}
and now taskCount will produce an array of the function names
and taskCollection will work when passed to async :)
Related
I wanted to write a understandable code in node.js, so I want to put some functions, which are used very often, into other node.js files, and access them from there.
So I get a function, which calls a function from another node.js file and in this other node.js file, also another one is called.
Important to know, if I put all in one file, the code works, so it should be an issue with module export and using functions in another file.
I have one file, getting quotes from a decentralised exchange. Looking like this (quoter_uni_v2.js):
module.exports = function quotes_uni_v2(tokenIn, tokenOut, amountIn, router) {
const quotedAmountOut = router.getAmountsOut(amountIn.toString(), [
tokenIn,
tokenOut,
]);
return quotedAmountOut;
};
And I am importing this function in my second helper file (quotes_5.js) (It is splitted in two files, because in the second one I have to call the function multiple times):
var quotes_uni_v2 = require("./quotes_uni_v2");
module.exports = async function (router1, router2, route, amount_wei) {
console.log(route);
var amount_Out = await quotes_uni_v2.quotes_uni_v2(
route[1],
route[2],
amount_wei,
router1
);
...
return (
Math.round(ethers.utils.formatEther(amount_Out[1].toString()) * 100) / 100
);
};
After that I try to call the function in my main.js:
const quotes_uni_v2 = require("./quotes_uni_v2");
const quotes_5 = require("./quotes_5");
async function calc(route) {
amountOut = await new quotes_5(
quickswap_router,
sushiswap_router,
route,
amount_wei
);
return amountOut;
};
But calling the quotes function does not work... The error is:
TypeError: quotes_5 is not a constructor...
Can someone help me?
Thanks!
I have a javascript file the that I'm running some node tasks in, and would like to be able to run them separately based on the terminal command I trigger.
For instance, my nodejs file myFile code could look like this:
const mysql = require('mysql');
const fs = require('fs');
const getDbData = () => {
...
...
}
const fileTransform = () => {
file transformation functionality
}
I'd like to be able to run each function separately, so that I can say node myFile.js getDbData in terminal. Do I need to export each of the functions to be able to do this?
You can supply command-line arguments to your script on the node command line. You receive them in the process.argv array. Your arguments start at index 2 (0 is the full path to node, 1 is the full path to your script).
So for instance:
switch (process.argv[2]) {
case "getData":
getData();
break;
case "etlData":
etlData();
break;
// ...
}
Note that it's true that your arguments start at index 2 even if a Node argument precedes your script on the actual command line. For instance:
node --use-strict your-script.js
...will still have the full path to node in process.argv[0] and the full path to your script in process.argv[1]. The --use-strict argument isn't in the array at all.
Or you can put your functions on an object and use the argument as a key:
function getData() {
// ...
}
function getData() {
// ...
}
const functions = {
getData,
etlData
};
const fn = functions[process.argv[2]] || () => { console.log("Invalid option"); };
fn();
Try using process.argv.
https://stackabuse.com/command-line-arguments-in-node-js/
parse the command line arguments and, for example, evaluate them with eval()
app.js
app.get('/save', function(req,res){
var switchInput = {
sw1: req.query.switch1,
sw2: req.query.switch2,
sw3: req.query.switch3,
sw4: req.query.switch4,
sw5: req.query.switch5,
sw6: req.query.switch6,
}
console.log(switchInput);
module.exports = switchInput
res.send(switchInput);
});
simulate.js
var mongoose = require('mongoose');
var suit = require('../app')
...
function batteryLife(t){
var elapsed = Date.now() - t;
t_remaining = fullTime - elapsed;
t_battery = secondsToHms(Math.floor(t_remaining/1000));
//console.log(Math.floor(elapsed/1000) + ' s');
console.log(suit.sw1);
return t_battery;
};
Console Log:
{ sw1: 'true',
sw2: 'true',
sw3: 'true',
sw4: 'true',
sw5: 'true',
sw6: 'true' }
--------------Simulation started--------------
undefined
undefined
undefined
undefined
--------------Simulation stopped--------------
When I try to access these values from a different js file they print as undefined I am using postman to simulate values
The values will log from here but print undefined from the other js file
Is there a way to correct this I'm not sure what I am doing wrong
the values are loading into "inputSwitch" but are not coming out on the simulate.js side
First of all, while using youre favorite webserver like Express, you request aka (req) will/could flow amongst middleware before reaching your specific endpoint. Which means your req params are accessible at anytime there which could help you for specific code logic middleware-the-core-of-node-js-apps.
I agree with vibhor1997a, you should not export something there, basically you only module.exports "things" at the end of a file, not something at run-time.
You could do if you really want to deal with switchInput in another file do :
do what vibhor1997a suggest (sync or async function)
have a middleware before reaching your endpoint
raised an event with your switchInput as argument example
You're exporting on an event which isn't a good idea. What you can do instead is call a function in the other file where you need values with the values.
Example
app.js
const simulate = require('./simulate');
app.get('/save', function(req,res){
var switchInput = {
sw1: req.query.switch1,
sw2: req.query.switch2,
sw3: req.query.switch3,
sw4: req.query.switch4,
sw5: req.query.switch5,
sw6: req.query.switch6,
}
simulate(switchInput);
res.send(switchInput);
});
simulate.js
module.exports = function(input){
//have all your functions and code that require input here
function foo(){...}
function bar(){...}
}
I have the following code that does not work currently.
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models.
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: true, force: false }).then(function() {
models.users.create({
name: 'joe',
loggedIn: true
}).then( task => {
console.log("saved user!!!!!");
});
process.exit();//close the nodeJS Script
}).catch(function(error) {
console.log(error);
});
sequelize-lib.js
var Sequelize = require('sequelize');
exports.getSequelizeConnection = function(stage){
var argv = require('minimist')(process.argv.slice(2)); //If this file is being used in a script, this will attempt to get information from the argument stage passed if it exists
//Change connection settings based on stage variable. Assume localhost by default.
var dbname = argv['stage'] ? argv['stage']+"_db" : 'localdb';
var dbuser = argv['stage'] ? process.env.RDS_USERNAME : 'admin';
var dbpass = argv['stage'] ? process.env.RDS_PASSWORD : 'local123';
var dbhost = argv['stage'] ? "database-"+argv['stage']+".whatever.com" : 'localhost';
//If state variable used during require overide any arguments passed.
if(stage){
dbname = stage+"_db";
dbuser = process.env.RDS_USERNAME
dbpass = process.env.RDS_PASSWORD
dbhost = "database-"+stage+".whatever.com"
}
var connection = new Sequelize(dbname,dbuser,dbpass, {
dialect: 'mysql',
operatorsAliases: false, //This gets rid of a sequelize deprecated warning , refer https://github.com/sequelize/sequelize/issues/8417
host: dbhost
});
return connection;
}
exports.setModels = function(connection){
//Import all the known models for the project.
const fs = require('fs');
const dir = __dirname+'/../models';
var models = {}; //empty model object for adding model instances in file loop below.
//#JA - Wait until this function finishes ~ hence readdirSync vs regular readdir which is async
fs.readdirSync(dir).forEach(file => {
console.log(file);
//Split the .js part of the filename
var arr = file.split(".");
var name = arr[0].toLowerCase();
//Create a modle object using the filename as the reference without the .js pointing to a created sequelize instance of the file.
models[name] = connection.import(__dirname + "/../models/"+file);
})
//Showcase the final model.
console.log(models);
return models; //This returns a model with reference to the sequelize models
}
I can't get the create command to work however with this setup. My guess is the variables must not be passing through correctly somehow. I'm not sure what I'm doing wrong?
The create command definitely works because if in the sequelize-lib.js I modify the setModels function to this...
exports.setModels = function(connection){
//Import all the known models for the project.
const fs = require('fs');
const dir = __dirname+'/../models';
var models = {}; //empty model object for adding model instances in file loop below.
//#JA - Wait until this function finishes ~ hence readdirSync vs regular readdir which is async
fs.readdirSync(dir).forEach(file => {
console.log(file);
//Split the .js part of the filename
var arr = file.split(".");
var name = arr[0].toLowerCase();
//Create a modle object using the filename as the reference without the .js pointing to a created sequelize instance of the file.
models[name] = connection.import(__dirname + "/../models/"+file);
models[name].create({
"name":"joe",
"loggedIn":true
});
})
//Showcase the final model.
console.log(models);
return models; //This returns a model with reference to the sequelize models
}
Then it works and I see the item added to the database! (refer to proof image below)
Take note, I am simply running create on the variable at this point. What am I doing wrong where the model object is not passing between files correctly? Weird part is I don't get any errors thrown in the main file?? It's as if everything is defined but empty or something and the command is never run and nothing added to the database.
I tried this in the main file also and no luck.
models["users"].create({
name: 'joe',
loggedIn: true
}).then( task => {
console.log("saved user!!!!!");
});
The purpose of this all is to read models automatically from the model directory and create instances that are ready to go for every model, even if new one's are added in the future.
UPDATE::
So I did another test that was interesting, it seems that the create function won't work in the .then() function of the sync command. It looks like it was passing it correctly though. After changing the front page to this...
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models using connection previosly created.
models["users"].create({
"name":"joe",
"loggedIn":true
});
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: true, force: false }).then(function() {
process.exit();//close the nodeJS Script
}).catch(function(error) {
console.log(error);
});
Doing this seems to get create to work. I'm not sure if this is good form or not though since the database might not be created at this point? I need a way to get it to work in the sync function.
Well I answered my question finally, but I'm not sure I like the answer.
var config = require('./libs/sequelize-lib.js');
var connection = config.getSequelizeConnection();//Choosing to not pass in variable this time since this should only run via script.
var models = config.setModels(connection);//Creates live references to the models using connection previosly created.
//Alter table as needed but do NOT force the change. If an error occurs we will fix manually.
connection.sync({ alter: false, force: false }).then( () => {
models["users"].create({
"name":"joe",
"loggedIn":true
}).then( user => {
console.log("finished, with user.name="+user.name);
process.exit();
}).catch( error => {
console.log("Error Occured");
console.log(error);
});
}).catch(function(error) {
console.log(error);
});
turns out that process.exit was triggering before create would occur because create happens async. This means that all my code will have to constantly be running through callbacks...which seems like a nightmare a bit. I wonder if there is a better way?
I have a list containing folders, and I'm trying to get the count of the total number of files in these folders.
I manage to retrieve a ListItemCollection containing my folders. Then it starts being... picky.
ctx is my ClientContext, and collection my ListItemCollection.
function countFiles()
{
var enumCollection = collection.getEnumerator();
while(enumCollection.moveNext())
{
currentItem = enumCollection.get_current();
var folder = currentItem.get_folder();
if (folder === 'undefined')
return;
ctx.load(folder, 'ItemCount');
ctx.executeQueryAsync(Function.createDelegate(this, function()
{
totalCount += folder.get_itemCount();
}), Function.createDelegate(this, onQueryFailed));
}
}
So it works... half of the time. If I have 6 items in my collection, I get 3 or 4 "The property or field 'ItemCount' has not been initialized" exceptions, and obviously my totalCount is wrong. I just can't seem to understand why, since the executeQueryAsync should not happen before the folder is actually loaded.
I'm very new to Javascript, so it may look horrid and be missing some essential code I didn't consider worthy of interest, feel free to ask if it is so.
Referencing closure variables (like folder in this case) from an asynchronous callback is generally a big problem. Thankfully it's easy to fix:
function countFiles()
{
function itemCounter(folder) {
return function() { totalCount += folder.get_itemCount(); };
}
var enumCollection = collection.getEnumerator();
while(enumCollection.moveNext())
{
var folder = enumCollection.getCurrent().get_folder();
if (folder === undefined) // not a string!
return;
ctx.load(folder, 'ItemCount');
ctx.executeQueryAsync(itemCounter(folder), Function.createDelegate(this, onQueryFailed));
}
}
(You don't need that .createDelegate() call because the function doesn't need this.)
Now, after that, you face the problem of knowing when that counter has been finally updated. Those asynchronous callbacks will eventually finish, but when? You could keep a separate counter, one for each query you start, and then decrement that in the callback. When it drops back to zero, then you'll know you're done.
Since SP.ClientContext.executeQueryAsync is an async function it is likely that the loop could be terminated before the first call to callback function completes, so the behavior of specified code could be unexpected.
Instead, i would recommend another and more clean approach for counting files (including files located under nested folders) using SharePoint JSOM.
How to count the total number of files in List using JSOM
The following function allows to count the number of list items in List:
function getItemsCount(listTitle, complete){
var ctx = SP.ClientContext.get_current();
var list = ctx.get_web().get_lists().getByTitle(listTitle);
var items = list.getItems(createQuery());
ctx.load(items);
ctx.executeQueryAsync(
function() {
complete(items.get_count());
},
function() {
complete(-1);
}
);
function createQuery()
{
var query = new SP.CamlQuery();
query.set_viewXml('<View Scope="RecursiveAll"><Query><Where><Eq><FieldRef Name="FSObjType" /><Value Type="Integer">0</Value></Eq></Where></Query></View>');
return query;
}
}
Usage
getItemsCount('Documents', function(itemsCount){
console.log(String.format('Total files count in Documents library: {0}',itemsCount));
});