Handling expensive task(nested for loop) on Node.js - javascript

First I write summarized pseudo code.
const ExcelSheet = new ExcelSheet() // excel.js library
const usersWithPlayedGames = await findAllUsers({ include: GameTable });
for (let i = 0; i < usersWithPlayedGames.length; i++) {
// Write some user data on Excel.
...
...
for (let j = 0; j < usersWithPlayedGames[i].length; j++) {
// Write some user's game on Excel
...
...
for (let k = 0; k < usersWithPlayedGames[i][j].length; k++) {
// Write some users's game's company data on Excel
...
...
}
}
}
res.send(ExcelSheet.toFile());
The actual code is pretty long.
And it take client req => res time almost 15sec.
I know my problem solution is not good.
I can do refactoring with this code.
But the real problem is it blocks another client request.
I search on google and find several solutions.
Node.js child process spawn.
Make with a callback function(I don't know exactly how to do).
Write more than a good algorithm.
What's the concept I'm missing from Node.js?
Please help me with a small favor.
Thank you.

You're porbably better off running this in a child process or somewhere else outside your web request thread, but if that's not an option, you could break the task up using something like setImmediate
const ExcelSheet = new ExcelSheet() // excel.js library
const usersWithPlayedGames = await findAllUsers({ include: GameTable });
const excelLoop = (index) => {
for (let j = 0; j < usersWithPlayedGames[index].length; j++) {
// Write some user's game on Excel
...
...
for (let k = 0; k < usersWithPlayedGames[index][j].length; k++) {
// Write some users's game's company data on Excel
...
...
}
}
if (index < usersWithPlayedGames.length) {
setImmediate(() => excelLoop(index + 1))
}
else {
res.send(ExcelSheet.toFile());
}
};
excelLoop(0);
Documentation for setImmediate

Related

Enmap (better-sqlite map) doesnt update until application is restarted

This is a code Snippet from a discord bot im working on
It needs to load up data from an Enmap, adds information to that Array and then pushes it back in.
It successfully pulls that data, does all the transformations and additions, etc
and successfully pushes that data
however, it cant pull the new data until the program is re-started. It gets saved but
let locationDataArray = await data.get(locationDataKey);
doesnt seem to do pull this new version of the map unless the program is restarted
I am fairly stumped
const Commando = require('discord.js-commando');
const levenshtein = require('fast-levenshtein');
const Enmap = require("enmap");
const data = new Enmap({
name:"locationdata"
});
const settingsMap = new Enmap({
name:"locationSettings"
})
module.exports = class addConnection extends Commando.Command{
constructor(client){
super(client, {
name:"addconnection",
group:"management",
aliases:["connect"],
memberName:"addconnection",
userPermissions:['ADMINISTRATOR'],
description:"adds a connection between two zones",
examples: ['$addconnection <zone1>,<zone2>'],
throttling: {
usages: 1,
duration: 5
},
args:[{
key:'destinations',
prompt:'what are the two areas you want to connect (seperate them with a comma , )',
type:'string'
}]
});
}
async run(msg,args){
//get our guilds Information
let guildID = msg.guild.id;
let locationDataKey = guildID+"_mapData";
let locationDataArray = await data.get(locationDataKey);
//Our Guild Settings
let settingMapKey = guildID+"_setting";
let settings = await settingsMap.get(settingMapKey);
//chiefly out npcRoleID
let npcRole = await msg.guild.roles.get(settings.npcRoleID);
let connectionArray = await args.destinations.toLowerCase().split(",");
for(var i = 0; i < connectionArray.length; i++){
//make sure the item is valid
var distance = levenshtein.get(connectionArray[i], locationDataArray[0].name);
var closestDistance = distance;
var closestWord = locationDataArray[0].name;
for(var j = 0; j < locationDataArray.length; j++){
distance = levenshtein.get(connectionArray[i], locationDataArray[j].name);
if (distance < closestDistance){
closestDistance = distance;
closestWord = locationDataArray[j].name;
}
}
//make sure all the areas are valid and good
if(closestDistance < (closestWord.length/2)){
connectionArray[i] = closestWord;
}
else{
msg.channel.send("those channels don't seem to exist").then( msg => {
msg.delete(10000);
});
return;
}
}
//our array of connections now only contains valid options
//loop over our location data array
for(var i = 0; i< connectionArray.length; i++){
for(var j = 0; j < locationDataArray.length; j++){
//when we hit one of out LDA that has the same name as something in the connectionArray
//stop add the rest of the connection array to its connections
if(locationDataArray[j].name === connectionArray[i]){
for(var k = 0; k < connectionArray.length; k++){
if(locationDataArray[j].name == connectionArray[k]){
}
else{
if(!locationDataArray[j].connections.includes(connectionArray[k])){
await locationDataArray[j].connections.push(connectionArray[k]);
}
//get the role for the connection and the current channel
let role = await msg.guild.roles.find(role => role.name === connectionArray[k]);
let currentChannel = await msg.guild.channels.find(channel => channel.name === locationDataArray[j].channelName);
//the connection can read but not type in its connection
currentChannel.overwritePermissions(
role,
{
'SEND_MESSAGES':false,
'VIEW_CHANNEL':true,
'READ_MESSAGE_HISTORY':true
}
)
data.set(locationDataKey, locationDataArray);
msg.channel.send("Connected "+locationDataArray[j].name+" and "+connectionArray[k]);
}
}
}
}
}
}
}
I know this question is old and I'm hoping you've resolved this issue a long time ago, but for posterity's sake, and because people might find this question later, I'm going to answer it anyways.
The reason this happens is that you're creating a new instance of Enmap in this file, and presumably you have it declared in other files too. This causes a new "client" for Enmap to be created, with its own individual cache, and it's not possible for Enmap to update those instances.
The solution is to either attach your Enmap to a variable you carry around (such as, for example, the Client), or to create a separate node module that you import wherever you need it. Both of those options are described in more details in the Enmap documentation: https://enmap.evie.dev/usage/using-from-multiple-files
Disclaimer: I am the author of Enmap

Waiting for *ngFor to build the page to perform DOM Manipulation

Community,
i am having some problems with my Angular 6 - App which i'm unfortunately not able to solve after hours of thinking & searching on google.
I've got a component that is displaying Meetings, Projects and Tasks in a Form. Those forms require a lot of data from different collections to select from (if the user wants to change something). All of those are being loaded in my ngOnInit:
ngOnInit() {
this.meetServ.getCompanyMeetings(false)
.subscribe(data => this.meetings = data as meeting[]);
this.deptServ.getCompanyDepartments()
.subscribe(data => this.depts = data as department[]);
this.CusServ.getCompanyCustomers()
.subscribe(data => this.customers = data as customer[]);
this.UserServ.getCompanyUsers()
.subscribe(data => {
this.users = data as user[];
})
}
When I'm done loading all the data, I want to set all of the dropdown menus to the right values. So I put a setDropdowns method in the subscription of the last http-request I am doing (also tried out of subscription).
The extend() Method is called by onclick event as soon as the User selects a Meeting:
extend(meetingID) {
document.getElementById("overlay").remove()
this.meetServ.getMeetingbyID(meetingID)
.subscribe(data => {
this.singleMeeting = data as meeting
this.init(); //gets all the projects by ID of selected Meeting
this.setMeetingAccessLevel();
this.setMeetingComments();
this.setDropdowns(); // tried like this
})
this.setDropdowns(); // and like this
}
and my setDropdowns() Method looks like this:
setDropdowns() {
var projectDivs = document.querySelectorAll("div.projectContent");
console.log(this.projects.length)
for (var i = 0; i < this.projects.length; i++) {
// Access Level
var opts = projectDivs[i].querySelector("#access").querySelectorAll("option");
(<HTMLOptionElement>opts[this.projects[i].accessLevel]).selected = true;
// Department
opts = projectDivs[i].querySelector("select#dept").querySelectorAll("option");
for (var j = 0; j < this.depts.length; j++) {
if (this.depts[j]._id == this.projects[i].department) {
opts[j + 1].selected = true
}
}
//Customer
opts = projectDivs[i].querySelector("select#proCustomer").querySelectorAll("option")
for (var k = 0; k < this.customers.length; k++) {
if (this.customers[k]._id == this.projects[i].customer) {
opts[k + 1].selected = true
}
}
//Official
var asd = (<HTMLSelectElement>projectDivs[i].querySelector("select#proinCharge")).querySelectorAll("option")
for (var l = 0; l < this.users.length; l++) {
/* if (this.users[l]._id == this.projects[i].inCharge) {
asd[l].selected = true
} */
}
}
}
But when I look at the console, this.projects.length is either 0 or 2 (which is the actual number of projects in that meeting). The thing is, when I get it to log 2, the projectDivs[i].querySelector("#access") part errors "Cannot read property 'querySelector' of undefined", which actually makes no sense, since projectDivs are generated like this:
<div class="projectContent" id="projectContent" *ngFor="let project of projects">
and console.log(this.projects.length) returns 2 earlier.
So projects are loaded but the HTML seems to not be loaded properly at that point.
Now for sure i googled this a lot and tried out stuff like jQuery document.ready and checked if document.readyState == complete, but it seems to only apply to the static document and not to the *ngFor generated elements.
So obviously my question in the end is: Is there any way to wait for all *ngFor generated elements or is there just a much easier way of doing this which is wasn't able to spot?
Thanks in advance for all the upcoming answers.
Greetings

Ember.JS do an operation every time the JSON is loaded

I am new to Ember and in my APP I need to add some operations depending on the JSON I get from the server.
This is currently my logic:
In the component.js
var foo = [];
didInsertElement() {
var data = this.get('content');
//then I do some transformations with data to obtain what I want and assign it to foo
data.forEach(function(option) {
list.push( JSON.parse(JSON.stringify(option)))
})
for(i = 0; i < list.length; i++) {
//some logic code with list
}
this.set('foo', someTransformationFromList);
for (i = 0; i < count; i++) {
this.get('content').push(jQuery.extend(true, {}, this.get('content')[0]));
}
for(i = 0; i < foo.length; i++) {
this.get('content')[i].set('string1', foo[i].text);
this.get('content')[i].set('id', foo[i].value);
}
}
So now the question is, I need to move this logic from didInsertElement to somewhere else so it gets executed every time I get my JSON no just the first time when the component is rendered. I have tried to use a serializer or a transform but I don't know if I can use any of them. Can you please give me any pointers about how to do this task?.
Thank you.

Extending a google spreadsheet into a google web app

I was coding using the google scripts, when I came across a problem I've been struggling with for a couple days now. I am using Code.gs (a default page in creating a web app in google), when I called in data from a google spreadsheet to try and display it on a webpage. I had no problems with calling in the data add storing it into a array but now I am struggling with trying to return it to my javascript code. Can this be done or is there something else I can do to fix it? My code is below.
function getContents()
{
var sheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1xum5t4a83CjoU4EfGd50f4ef885F00d0erAvUYX0JAU/edit#gid=0&vpid=A1');
var range = sheet.getDataRange();
var values = range.getValues();
for (var i = 0; i < values.length; i++) {
var education = [];
for (var j = 0; j < values[i].length; j++) {
if (values[i][j]) {
if(j==1){
education[education.length] = values[i][j];
}
}
}
}
Logger.log(education);
return education;
}
From that Code.gs code i want it to return it to a Javascript function that says this:
function onNew(){
var input = google.script.run.getContents();
for(var = 0; i<input.length; i++)
{
$("#main").append("<div id='class'>"+input[i]+"</div>);
}
}
and whenever I try to run it says that it causes an error because it is undefined. Thanks in advance! Anything helps!
You need to use the withSuccessHandler(). Your variable input will not receive the return from google.script.run.getContents()
Separate out the client side code into two functions:
HTML Script
function onNew() {
google.script.run
.withSuccessHandler(appendEducationHTML)
.getContents();
};
function appendEducationHTML(returnedInfo) {
console.log('returnedInfo type is: ' + typeof returnedInfo);
console.log('returnedInfo: ' + returnedInfo);
//If return is a string. Convert it back into an array
//returnedInfo = returnedInfo.split(",");
for (var = 0;i < returnedInfo.length; i++) {
$("#main").append("<div id='class'>"+returnedInfo[i]+"</div>);
};
};

Synchronization problem in functions of Javascript

I am developing an application,In that application i have many functions that return us server data.. I have 40 function that needs to be executed 1 by 1 for synchronization to take place.SO i have taken a single function and placed my 40 functions in it... Now the problem is that some of these function skip performing database operations
My one out of 40 function to perform this is
function sync_down_client_info(){
try {
parent.parent.parent.stmt_select.text = "select query";
parent.parent.parent.stmt_select.execute();
parent.parent.parent.result = parent.parent.parent.stmt_select.getResult();
if (parent.parent.parent.result.data != null) {
for (var i = 0; i < parent.parent.parent.result.data.length; i++) {
var admin_id = parent.parent.parent.admin_id;
var sync_client_date = parent.parent.parent.result.data[i].last_sync_client;
// alert(admin_id+"======="+ sync_client_date);
GATEWAY = 'http://projects/one_acc/flashservices/gateway.php';
conn = new parent.parent.parent.air.NetConnection();
conn.connect(GATEWAY);
response = new parent.parent.parent.air.Responder(clientresult, clientFault);
conn.call('down_client_info', response,admin_id,sync_client_date);
response = new parent.parent.parent.air.Responder(clientserverdatetimeResult, clientserverdatetimeFault);
conn.call('check_datetime', response);
}
};
and clientresult function is
function clientresult(e)
{
for (var i=0; i<e.length; i++) {
//alert(e.length);
parent.parent.parent.stmt_select.text="select query ;
parent.parent.parent.stmt_select.execute();
parent.parent.parent.result = parent.parent.parent.stmt_select.getResult();
if(parent.parent.parent.result.data!=null){
parent.parent.parent.stmt_insert.text= "update client_info ;
parent.parent.parent.stmt_insert.execute();
}
can anyone tell me how to do this syncing?
jQuery has constructs like the AjaxQueue to avoid these race conditions you are seeing. I'd advise using these plugins.
Refer http://plugins.jquery.com/project/ajaxqueue

Categories

Resources