dealing with asynchronous updating of an object - javascript

const globalObject = {};
const varAList = [.....];
const varBList = [.....];
const varA = await Promise.all(
varAList.map(
async (aObject) => {
const toReturn = [];
for (const bObject of varBList) {
if (!globalObject[bObject.id]) {
console.log(`getting ${bObject.id}`);
globalObject[bObject.id] = await getObject(bObject.id);
}
if (globalObject[bObject.id].important == true){
toReturn.push(globalObject[bObject.id].something)
}
}
return {aObject.id: toReturn}
}
)
);
The problem with this code is that the line console.log(``getting ${bObject.id}``) will be repeated for the same id since the varAList is going in parallel. How do I make sure that I am asynchronously checking this object and not remaking a request if it already exists?
Any solutions?

You question is confusing, If understand your correctly, You don't want to iterate over varAList?
const globalObject = {};
const varAList = [.....];
const varBList = [.....];
async function* genarateResult() {
for (const bObject of varBList) {
let aObject = varAList.shift();
let obj = globalObject[bObject.id];
let toReturn = [];
if (!obj) {
console.log(`getting ${bObject.id}`);
globalObject[bObject.id] = await getObject(bObject.id);
}
if (obj.important == true) {
toReturn.push(obj.something)
}
yield { [aObject.id]: toReturn };
}
}
for await (const val of genarateResult()) {
console.log(val);
}
Does it answer you question? or what I missed?

Related

Having problem in doing operations on values after asynchronous calls

Here i'm calculation two values color and number after calculation i'm updating the values but this is not working
gameSchema.methods.computeBets = async function(){
const game = this;
const greenMultiplier = 2;
const voiletMultiplier = 4.5;
const redMultiplier = 2;
try {
const { gameType } = game;
let model = Schema;
gameType === 'Fast Parity' ? model = FastParity : model = SlowParity;
const gameDetails = await model.findOne({ gameId: game._id });
const { allPlayerBettedBetId } = gameDetails;
let color = {};
let number = {};
await Promise.all(allPlayerBettedBetId.map(async(bet) => {
const betdetails = await UserBet.findById(bet._id);
const { colorBettedOn, numberBettedOn,amountBetted,betType} = betdetails;
// console.log(colorBettedOn, numberBettedOn, amountBetted);
if(betType==='color')
color[colorBettedOn] = color[colorBettedOn] ? color[colorBettedOn] + amountBetted : amountBetted;
if(betType==='number')
number[numberBettedOn] = number[numberBettedOn] ? number[numberBettedOn] + amountBetted : amountBetted;
}));
console.log(color, number);
color.forEach(item => {
if (item.green)
item.green *= greenMultiplier;
if (item.red)
item.red *= redMultiplier;
if (item.voilet)
item.voilet *= voiletMultiplier;
});
let colorCombination = {
"green": color.green,
"green/voilet": color.green+(color.voilet/2),
"red/voilet": color.red+(color.voilet/2),
"red": color.red
};
number.forEach(item => {
item.value *= 2;
})
console.log(number,colorCombination)
return { number, colorCombination };
after await Promise.all console.log(number,color) is working but when i'm updating the values they are not working.I'm not good at async function as i don't have much exprience in that.what mistake i'm doing in the forEach functions

Why is my function not returning the desired value, however, console.log() does the job?

I know this question might be a little basic but I think I am lacking some important fundamental concept. I am coding in node.js, and I have a function getPostInfo(). In the end, I have a return statement that returns the object that I created. However, when I run it on PowerShell I get no output. Furthermore, when I console.log(object) I get the required answer.
If someone knows what I might be doing wrong, let me know.
P.S. - The major chunks of code in the middle can be skipped as they are just to get information of a webpage
const cheerio = require('cheerio');
const axios = require('axios');
let object = {};
const getPostInfo = async () => {
const {data} = await axios.get('https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv');
// console.log(data)
const $ = cheerio.load(data);
const titles = [];
const date = [];
const runtime = [];
const rating = [];
const metascore = [];
const votes = [];
const grossEarning = [];
$('h3 a').each((i, el) => {
titles[i] = $(el).text().trim();
})
$('h3 .lister-item-year').each((i, el) => {
date[i] = $(el).text();
})
$('.runtime').each((i, el) => {
runtime[i] = $(el).text().trim();
});
$('.ratings-imdb-rating').each((i, el) => {
rating[i] = $(el).text().trim();
})
$('.ratings-bar').each((i, el) => {
if ($(el).find('.ratings-metascore .favorable').length > 0) {
metascore[i] = $(el).find('.ratings-metascore .favorable').text().trim();
}
if ($(el).find('.ratings-metascore .mixed').length > 0) {
metascore[i] = $(el).find('.ratings-metascore .mixed').text().trim();
}
})
const nv = [];
$('.sort-num_votes-visible').each((i, el) => {
if($(el).find('span')){
// text-muted has text 'votes:', however we need the number of votes which is in next() span tag
nv[i] = $(el).find('.text-muted').next().text();
votes[i] = nv[i].split('$')[0];
grossEarning[i] = '$' + nv[i].split('$')[1];
}
})
for (let i = 0; i < 50; i++) {
object[i] = {
title: titles[i],
date: date[i],
runtime: runtime[i],
rating: rating[i],
metascore: metascore[i],
votes: votes[i],
grossEarning: grossEarning[i]
};
}
// This does not work but console.log(object) gives me a list of objects
return object
// console.log(object);
}
getPostInfo()

Is there a way to convert an object key containing an array to an object?

Here is my current object:
{"or_11[and_3][gt#last_closed_sr]":"2019-06-18"}
I would like it to look like:
{
"or_11": {
"and_3": {
"gt#last_closed_sr": "2019-06-18",
}
}
}
What is the best way to do this?
In general the answer to poorly formatted data is to fix the formatter, not to implement a parser. However, I've worked with systems that encode data like that, so here's a parser.
function parseSquareSeparatedData(data) {
const result = {};
Object.keys(data).forEach((key) => {
const keyParts = key.replace(/]/g, "").split("[");
const last = keyParts.pop();
let resultPointer = result;
keyParts.forEach((keyPart) => {
if (!(keyPart in resultPointer)) {
resultPointer[keyPart] = {};
}
resultPointer = resultPointer[keyPart];
})
resultPointer[last] = input[key];
})
return result;
}
let str = '"or_11[and_3][gt#last_closed_sr]":"2019-06-18"';
let first = str.replace(/"/g, '').match(/\w+/)[0];
let pattern = /\[(.+?)\]/g;
let matches = [];
let match;
while(match = pattern.exec(str)) {
matches.push(match[1])
}
let val = str.replace(/"/g, '').split(':')[1];
let obj = {
[first]: {
[matches[0]]: {
[matches[1]]: val
}
}
}
console.log(obj)

How to reduce congnitive complexity from 10 to 6 for a function in JavaScript

I have a trouble reducing the cognitive complexity here for this function.I tried to separate the contents inside the forEach as another function and by calling it in getCars function but failed. Could anyone please help?
const getCars = (cars, config, types) => {
const {
carName
} = types;
const carObject = {};
const carsRange = () => {}
let carRange = carsRange(cars);
Object.entries(cars).forEach(([key, value]) => {
if (key === 'sedan' && value) {
const carRangeVal = value.split(' ');
const year = carRangeVal[1];
const model = carRangeVal[0].substring(1, 2);
carRange = generateCarRange(year, model);
}
if (key === 'suv' && value) {
const carRangeVal = value.split(' ');
const year = carRangeVal[1];
const model = carRangeVal[0];
carObject['model'] = true;
carRange = checkYear(year, model);
}
if (value) {
carObject[key] = value;
}
});
if (
config.header === 'TEST A' ||
config.header === 'TEST B'
) {
carObject['carName'] = carName[0].id;
}
carObject['configName'] = config.header;
carObject['contractStartDate'] = carsRange[0];
carObject['contractEndDate'] = carsRange[1];
return carObject;
};
console.log(getCars({}, {}, {}));
You could shorten the code in the .forEach function:
[year,model] = value.split(' '); // destructuring assignment
if (key === 'sedan' && value) {
model = model.substring(1, 2);
carRange = generateCarRange(year, model);
}
if (key === 'suv' && value) {
carObject['model'] = true;
carRange = checkYear(year, model);
}
It would be helpful to know the input data.
I have an inkling that you may be better off using .map() instead of .forEach().
Why do you have two functions (generateCarRange() and checkYear()) to get carRange?
(I find it helpful to use Hungarian Notation to always know the type of my variables.)

How do I assign a variable defined in a synchronous scope in an async function?

I am trying to write a GUI frontend that uses a service to get data about the system. I am using a net.Socket for the client end of this. I want to be able to access certain variables assigned in the data event handlers in other modules but the assignment does not stay after that callback function finishes.
Problematic code:
client.on('data', (data) => {
var array = [...data];
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
module.exports.hasBattery = lastBatteryJSON.hasBattery == 'true';
module.exports.isCharging = lastBatteryJSON.isCharging == 'true';
module.exports.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
Those three exported variable assignments don't actually work, the variables always either stay undefined or their default values outside of the function. I tried using a Promise to solve this problem but got the same result. I'm at a loss and I can't find any other questions or forum posts that solve this problem.
EDIT
I do not have the option of moving the code that depends on those variables into the callback. In order to do that I would have to wait for the data every frame and flood the server as a result.
As apple commented; you can export an object and mutate it every time you receive data:
const data = {};
client.on('data', (data) => {
var array = [...data];
array.splice(0, 2);
for (var i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
//mutate the data object
data.hasBattery = lastBatteryJSON.hasBattery == 'true';
data.isCharging = lastBatteryJSON.isCharging == 'true';
data.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
//export the data object
module.exports.batteryData = data;
Or as CertainPerformance answered you can have the caller decide when to ask for the information and provide a promise.
Here is an extended version of CertainPerformance answer that listens to error as well so a promise can be rejected and cleans up the event listeners when promise is resolved or rejected:
//wrapper for client.on to add and remove event listeners
const listeners = (function(){
var listenerCounter = -1;
const listeners = [];
const triggerEvent = event => data =>{
listeners.filter(
listener=>listener[2] === event
).forEach(
listener=>listener[1](data)
);
};
client.on('data', triggerEvent("data"));
client.on('error', triggerEvent("error"));//assuming you have an error event
return {
add:(event,fn)=>{
listenerCounter = listenerCounter + 1;
if(listenerCounter>1000000){
listenerCounter=0;
}
listeners.push([listenerCounter,fn,event]);
return listenerCounter;
},
remove:num=>{
listeners = listeners.filter(
listener=>{
num !== listener[0];
}
)
}
}
}());
//convert data to object or false
const getObjectFromData = data => {
var array = [...data];
var dataInBuffer="";
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
return {
hasBattery : lastBatteryJSON.hasBattery == 'true',
isCharging : lastBatteryJSON.isCharging == 'true',
lastBatteryReading : parseFloat(lastBatteryJSON.batteryLife)
};
}
return false;
}
//export this function
const getBatteryData = () =>
new Promise((resolve,reject) => {
const removeListeners = ()=>{
listeners.remove(okId);
listeners.remove(errorId);
}
const okId = listeners.add(
"data",
data=>{
const resultObject = getObjectFromData(data);
if(resultObject){
resolve(data);
removeListeners();//clean up listeners
}else{
//not sure of on data is triggered multiple times by client.on.data
// if it is then at what point do we need to reject the returned promise?
}
}
)
const errorId = listeners.add(
"error",
error=>{
reject(error);
removeListeners();//clean up listeners
}
)
});
//you can call getBatteryData like so:
//getBatteryData()
// .then(batteryData=>console.log(batteryData))
// .catch(error=>console.warn("an error getting battery data:",error))
Your module should export a function that returns a promise that returns the desired values. Also, use const and not var when possible:
let resolveObj;
const haveData = new Promise((resolve) => {
let resolved = false;
client.on('data', (data) => {
const array = [...data];
array.splice(0, 2);
for (let i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
const {
hasBattery,
isCharging,
batteryLife,
} = JSON.parse(dataInBuffer.split(';')[1]);
resolveObj = {
hasBattery: hasBattery === 'true',
isCharging: isCharging === 'true',
lastBatteryReading: Number(batteryLife),
};
if (!resolved) resolve();
resolved = true;
}
dataInBuffer = '';
});
});
const getData = () => haveData.then(() => resolveObj);
module.exports = getData;
Then consume with
moduleFunction().then(({ hasBattery, isCharging, lastBatteryReading }) => {
// do something with results
});
If called before resolveObj is populated, the promise will wait until the first client.on('data' to resolve. After that, the function will return a promise that resolves immediately to the current value of resolveObj (which will be properly updated on client.on('data')

Categories

Resources