In a promise, I want to assign a value to the property of several objects created from a class (in a loop), but when executing the function and doing the .then(() => console.log(r)) thing, the r does was not modified to what the promise promised me it would.
Here:
function assignSentenceImageDescription () {
return new Promise((resolve, reject) =>
{
assigningWordsPartOFSpeech().then((r) => {
JSON.parse(r).sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
}
)
resolve(r);
}
);
}
);
}
On the line
sentence.imageDescription = sentenceImageDescription;
I try to assign the image description of each of the sentences iterated over, but when I do
assignSentenceImageDescription().then(r => console.log(r));
the r object still does not have each of its sentences's imageDescription property modified to the value the array sentenceImageDescription has, which is what the assignSentenceImageDescription() function is intended to do.
Refactor your code as follows:
Note: you don't need a Promise constructor since assigningWordsPartOFSpeech returns a Promise that you can work with (and return)
Set sentences = JSON.parse(r).sentences;
Now you can iterate through sentences as you already do, then simply return sentences in the .then - and you're done
function assignSentenceImageDescription() {
return assigningWordsPartOFSpeech().then((r) => {
const data = JSON.parse(r);
data.sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
});
return data;
});
}
Related
I have an Array that is passed through a forEach loop. Each item is then passed through and is checked for a specific order or characters. If it is true the characters are meant to be removed from the string, then it is supposed to be looped back over with the new string. (Old string minus the checked characters)
My problem is the fact that all the functions are still locally holding the old String (Before the characters were removed). How can I pass the new string into the functions without resetting the value "deleteStringData" currently holds?
let dnaArray = ["ACATATAGACATACGT","AAAAAATACATAGTAGTCGGGTAG","ATACATCGGGTAGCGT"];
dnaStrand = "";
//SORT THROUGH EACH ITEM IN ARRAY
dnaArray.forEach((dnaStrand, index) => {
if (findDna(dnaStrand)) {
console.log("Case #" + index + " " + dnaStrand + ": YES");
}
else {
console.log("Case #" + index + " " + dnaStrand + ": NO");
};
});
function findDna(dnaStrand){
if (findHead(dnaStrand)){
if(findBody(dnaStrand)){
console.log("dna");
return true;
}
}
else {
return false;
}
};
function findHead(dnaStrand){
if (findGlobe(dnaStrand)){
if (findEyeSpots(dnaStrand)) {
return true;
}
}
else{
return false;
}
};
function findBody(dnaStrand){
if (findGlobe(dnaStrand) && findLegs(dnaStrand)) {
return true;
}
else {
return false;
}
};
function findGlobe(dnaStrand){
if(findMatch(dnaStrand, /(A+(TAC|CAT)A)/)){
return true;
}else{
console.log("No Globe");
}
};
function findEyeSpots(dnaStrand){
if(findMatch(dnaStrand, /T(CG*T)*AG/)){
return true;
}else{
console.log("No Eyes");
}
};
function findLegs(dnaStrand){
if(findMatch(dnaStrand, /CG*T/)){
return true;
}else{
console.log("No Legs");
}
};
function findMatch (dnaStrand, regex) {
dnaStrand = String(dnaStrand);
let isMatch = dnaStrand.match(regex);
isMatch = String(isMatch[0]);
//console.log(isMatch);
if (isMatch) {
deleteStringData(dnaStrand, isMatch);
return true;
}
else {
return false;
}
};
function deleteStringData (dnaStrand, string) {
dnaStrand = dnaStrand.replace(string, "");
};
Sorry, but I am sort of busy and don't have the time to read through all your code. Based on your question, I can give you some basic guidance.
make sure your function takes a parameter. If each time the end result is not satisfactory or you just want to repeat it again, do an if statement, and if you're going to repeat it, do yourFunction(endresult) inside. If you worry about lag, consider making your end result a global variable and setting a setTimeout to your function.
If you declare the test functions inside the loop you can act on single instance of the strand in that scope without needing to pass any references around.
You can also use Promises to chain the tests.
This may not be the most efficient method but it is very readable.
(async () => {
const strands = [
'ACATATAGACATACGT',
'AAAAAATACATAGTAGTCGGGTAG',
'ATACATCGGGTAGCGT'
];
const results = [];
async function analyze(strand, index) {
let result = {
'case' : parseInt(index), strand
};
function findMatch(pattern) {
return new Promise((resolve, reject) => {
let m = strand.match(pattern)[0];
if (m) {
strand = strand.replace(m, '');
resolve(true);
} else {
reject();
}
});
}
function findHead() {
return findGlobe().then(findEyes);
}
function findBody() {
return findGlobe().then(findLegs);
}
function findGlobe() {
return findMatch(/(A+(TAC|CAT)A)/);
}
async function findEyes() {
result.eyes = await findMatch(/T(CG*T)*AG/);
}
async function findLegs() {
result.legs = await findMatch(/CG*T/);
}
function found() {
result.dna = true;
}
function failed() {
result.dna = false;
}
function done() {
results.push(result);
}
await Promise.all([
findHead(),
findBody()
]).then(found)
.catch(failed)
.finally(done);
}
for (let i in strands) {
await analyze(strands[i], i);
}
console.log(results);
})();
So basically im working on a cron job in my app that fires every 3 hours and updating users 'score' by calling the RiotApi
basically the function so far
exports.updatePlayersPoints = async () => {
console.log('STARTED UPDATING');
try {
const players = await UserLadder.findAll();
await Promise.all(
players.map(async (player) => {
const p = await RiotAccount.findOne({
where: {
userId: player.userId,
},
include: RiotRegions,
});
const beginTime = new Date(player.dataValues.createdAt);
let data;
try {
const res = await axios.get(
`https://${
p.dataValues.riot_region.dataValues.name
}.api.riotgames.com/lol/match/v4/matchlists/by-account/${
p.dataValues.accountId
}?queue=420&beginTime=${beginTime.getTime()}&api_key=${
process.env.RIOT_KEY
}`
);
data = res.data;
} catch (error) {
if (!error.response.status === 404) {
console.error(error);
}
}
if (!data) {
return;
}
let totalScore = player.dataValues.userPoints;
await Promise.all(
data.matches.map(async (match, i) => {
if (i < 15) {
const { data } = await axios.get(
`https://${p.dataValues.riot_region.dataValues.name}.api.riotgames.com/lol/match/v4/matches/${match.gameId}?api_key=${process.env.RIOT_KEY}`
);
const calculateScore = () => {
return new Promise((resolve) => {
const { stats } = _.find(
data.participants,
(o) => o.championId === match.champion
);
const killsPts = stats.kills * 2;
const deathPts = stats.deaths * -1.5;
const assistsPts = stats.assists;
const wardsPts = stats.wardsPlaced / 4;
const firstBloodPts = stats.firstBloodKill ? 3 : 0;
const firstBloodAssistPts = stats.firstBloodAssist ? 3 : 0;
const firstTowerPts = stats.firstTowerKill ? 2 : 0;
const firstTowerAssistPts = stats.firstTowerAssist ? 2 : 0;
const score =
killsPts +
deathPts +
assistsPts +
wardsPts +
firstBloodPts +
firstBloodAssistPts +
firstTowerPts +
firstTowerAssistPts;
totalScore += score;
resolve();
});
};
await calculateScore();
}
})
);
const user = await UserLadder.findOne({
where: {
userId: player.userId,
},
});
user.userPoints = parseFloat(totalScore);
user.lastGameId = data.matches[0].gameId;
await user.save();
})
);
console.log('FINISHED UPDATING');
} catch (error) {
console.error(error);
}
};
Basically it just looks up the table userladder to find the players that are signed to the ladder and for each one of these players it fires a map function that makes a request to the riotapi to get the match history of this player and then later make an inside map function to map each one of these matches.
but basically I updated it to now keep track of the game id of the last call before 3 hours so it doesn't have to make request that was already done.
user.lastGameId = data.matches[0].gameId;
but now in my second map function that maps the matches I wasn't it so that if the last game from my database matches the game id that currently being mapped I want to stop the map function and not continue this record or the ones after because it also means they all have been already counted.
but I can not seem to find a way to do it.
i tried using break; but it didn't work
any ideas?
using for loop
I tried a small test with for loop so I tried
for (let i = 0; i < 15; i++) {
await new Promise(async (resolve, reject) => {
const match = data.matches[i];
console.log(match);
resolve();
if (i === 1) {
break;
}
});
}
but I still go the same error
SyntaxError: Illegal break statement
Instead of trying to "break" a map, you should filter the matches that you want to process before you execute the map.
Something like this:
await Promise.all(
const filteredMatches = data.matches.filter(match => match.gameId > previousId);
filteredMatches.map(async (match, i) => { ...
More on filter() in javascript.
Edit: If generated id's are random and are not ordered, you can store all previous id's in a Set, and then just ask if it has been previously added
await Promise.all(
const filteredMatches = data.matches.filter(match => mySet.has(match.gameId));
filteredMatches.map(async (match, i) => { ...
More on Set in javascript.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Calling a class prototype method by a setInterval event
(3 answers)
Closed 5 years ago.
I'm trying to take a more OOP approach to the way I write Javascript. As a relative beginner, I'm a bit confused by the error I'm getting Uncaught TypeError: this.getCoinSelOption is not a function at getBTC. I have the this keyword calling other methods in my class, but it does not want to call a different method on an interval. What should I do here?
How I'm calling:
coins.getBTC(abbr)
setInterval(coins.getBTC, 10000)
I can call the method initially, but when I need to redetermine the abbreviation of the BitCoin, I go to the global const abbr and update the value based on what's selected through the select tag
class Coins {
constructor () {
this.coinOptions = document.getElementById('coinOptions');
}
getCoinObjects (url) {
let promise = new Promise((resolve, reject) => {
fetch(url)
.then(response => {
resolve(response.json().then(data => {
let coins = []
for (var key in data.Data) {
coins.push(data.Data[key])
}
//this is working here!
this.getCoinOptions(coins)
return coins
}))
})
.catch(response => {
reject('“Api call failed!”')
})
})
.then(response => {
return coins
})
.catch(response => {
console.log('error: ' + response)
})
}
getCoinOptions(keys) {
for (var i = 0; i < keys.length; i++) {
var opt = document.createElement('option');
opt.value = keys[i].Symbol
opt.innerHTML = keys[i].CoinName
this.coinOptions.appendChild(opt)
}
}
getBTC(abbr) {
if (abbr === undefined) {
//this not working here
abbr = this.getCoinSelOption(this.coinOptions)
console.log('this is the abbr' + abbr)
}
axios.get('https://min-api.cryptocompare.com/data/pricemulti?fsyms='+abbr+'&tsyms=USD')
.then(res => {
console.log(abbr)
const cryptos = res.data[abbr].USD
price.innerHTML = '$'+cryptos.toLocaleString('en')
if (targetPrice.innerHTML != '' && targetPriceVal < res.data[abbr].USD) {
const myNotification = new window.Notification(notification.title, notification)
}
return abbr
})
setTimeout(this.getBTC, 10000)
}
getCoinSelOption(sel) {
console.log('called')
loadBounce.classList.add('loading')
this.getBTC(sel.options[sel.selectedIndex].value)
setTimeout(function() {
loadBounce.classList.remove('loading')
}, 900)
return sel.options[sel.selectedIndex].value
}
}
On my Node JS backend I run this method.
var locations = [];
exports.constructionsiteParser = function constructionsiteParser(response){
var timestamp = new Date().toDateInputValue();
const $ = cheerio.load(response);
$('situation').each( function(){
var situation = [];
$(this).find('situationRecord').each( function(i){
var startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
var endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
var overallStartTime = $(this).find('overallStartTime').text();
var overallEndTime = $(this).find('overallEndTime').text();
if((startLocationCode != '') && new Date(timestamp) >= new Date(overallStartTime) && new Date(timestamp) <= new Date(overallEndTime) ){
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then( values =>{
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then( function(obj){
console.log("before push", situation);
situation.push(obj);
console.log("after push", situation);
return situation;
}, handleError);
}
})
console.log("from outter", situation.length);
if(situation.length > 0){ //if situation is not empty
locations.push(situation);
}
})
console.log(locations);
}
The console.log("from outter", situation.length); at the bottom prints always 0
also the console.log(locations) is empty
This is a part of the log:
...
from outter 0
from outter 0
from outter 0
from outter 0
from outter 0
[]
before push []
after push [....
I think this happens because the node server runs the bottom part before the inner each loop finishes. So I want to make it more snychronized. What I want to do is something like:
outer each{
//run this first
inner each{
.....
}
//if inner each is done run this
if(...){}
}
But I don't know how to put this in the correct syntax.
I have tried it with nested Promises but it doesn't work.
you can return this promise. deal it at caller
You can make use of async.eachOf(). I took a different approach in making your code synchronous. Hope it helps you.
'use strict';
let locations = [];
exports.constructionsiteParser = function constructionsiteParser(response) {
const $ = cheerio.load(response);
$('situation').each(function () {
let situation = [];
async.eachOf($(this).find('situationRecord'), function (value, key, callback) {
innerLoop(callback);
}, function (err, situation) {
if (err) {
return console.error(err.message);
}
console.log("from outter", situation.length);
// this will run only if the inner loops completes
if (situation.length > 0) { //if situation is not empty
locations.push(situation);
}
});
});
console.log(locations);
};
function innerLoop(callback) {
let startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
let endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
let overallStartTime = $(this).find('overallStartTime').text();
let overallEndTime = $(this).find('overallEndTime').text();
if (isInvalid(startLocationCode, overallStartTime, overallEndTime)) {
return callback('some error msg');
}
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then(values => {
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then((obj) => {
return callback(null, obj);
}).catch((err) => {
console.log('err', err.stack);
return callback(err);
});
}
function isInvalid(startLocationCode, startTime, endTime) {
let timestamp = new Date().toDateInputValue();
let isEmptyCode = startLocationCode === '';
let isYetToStart = new Date(timestamp) < new Date(startTime);
let isOver = new Date(timestamp) > new Date(endTime);
return isEmptyCode || isYetToStart || isOver;
}
You should take a deeper look into promises because they are the way to go for synchronous operations. Maybe try to merge your code into functions.
First of all, I can't find a suitable title for this question - please feel free to edit.
I have the following function that reads objects from indexDb,
loadNeededParcels = property => {
const dbResults = [];
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
console.log("open cursor error ", e);
};
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
return dbResults;
}
};
};
Now when I call this function with a simple function call, for example:
console.log(loadNeededParcels('hasData'))
The console log is undefined. I am guessing this happens because the function does not wait for the cursor to finish and return the dbResults variable?
So my question is this - how can I re-write this function as a promise, or rather to wait for the readTransaction.openCursor().onsucess to trigger?
So the expected result is for the function to actually return the values read from the database before exiting.
I am using cursors since the .getAll() the method is not supported in IE.
A simple solution that I ended up using:
loadNeededParcels = property => {
return new Promise((resolve, reject) => {
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
reject(e);
};
const dbResults = [];
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
resolve(dbResults);
}
};
});
};
Try something like this. Do not call openCursor twice, that creates two requests.
function loadNeededParcels(db, property) {
return new Promise(function(resolve, reject) {
var results = [];
var tx = db.transaction('parcelData');
tx.onerror = function(event) {
reject(tx.error);
};
var store = tx.objectStore('parcelData');
// Open a cursor over all items
var request = store.openCursor();
request.onsuccess = function(event) {
var cursor = request.result;
if(cursor) {
var value = cursor.value;
if(value) {
// Only append defined values to the array
results.push(cursor.value);
}
cursor.continue();
} else {
resolve(results);
}
};
});
}
loadNeededParcels(db, 'hasData').then(function(results) {
console.log('Results', results);
}).catch(function(error) {
console.error(error);
});