force JQuery $(document).ready to wait for promise to resolve - javascript

So I am having some trouble assigning a variable to the response of a fetch request. I essentially want to run a fetch request to my API, which will return a value. I then want to assign that value to an element within the html.
When I run what I have, I can see that the value is being assigned as PromiseĀ {<pending>}
A few solutions I have seen is that I could run multiple $(document).ready(function(){});'s but I think it is still not resolving the promise before moving onto the next $(document).ready(function(){});
Here is what I have so far
var bal;
$(document).ready(function(){
function getCookie(name){
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if(parts.length == 2) return parts.pop().split(";").shift();
}
var decoded = jwt_decode(getCookie("JWT"));
console.log(decoded.email);
const url = "http://localhost:3001/wallets/" + decoded.email;
bal = fetch(url).then((resp) => {
return resp.json();
}).then((data) => {
console.log(data[0].balance);
return data[0].balance;
}).catch((error) => {
console.log(error);
});
$(document).trigger("my-event");
});
$(document).on("my-event", function(){
console.log(bal);
console.log(parseFloat(bal));
var oMain = new CMain({
win_occurrence:30, //WIN PERCENTAGE.SET A VALUE FROM 0 TO 100.
slot_cash: 100, //THIS IS THE CURRENT SLOT CASH AMOUNT. THE GAME CHECKS IF THERE IS AVAILABLE CASH FOR WINNINGS.
min_reel_loop:0, //NUMBER OF REEL LOOPS BEFORE SLOT STOPS
reel_delay: 6, //NUMBER OF FRAMES TO DELAY THE REELS THAT START AFTER THE FIRST ONE
time_show_win:2000, //DURATION IN MILLISECONDS OF THE WINNING COMBO SHOWING
time_show_all_wins: 2000, //DURATION IN MILLISECONDS OF ALL WINNING COMBO
money:bal, //STARING CREDIT FOR THE USER
...
I am still a little confused about how promises work and how to forcefully make things wait for them to resolve.

Just to provide an update and to close this question. This is the method I ended up with to resolve the issue.
const finishedPromise = fetch(url).then(r=r.json()).then(data => {
return data[0].balance'
});
Then within the function where I assign the value I simply used
money: await finsihedPromise

Related

Having trouble slowing down fetch in javascript so as to avoid 429 errors

I wonder if someone could help me.
I have a non-professional development license for a reverse geocoder within a javascript program where I am only allowed to do two requests
per second otherwise I get a 429 error. I have 3 sets of co-ordinates I wish to feed into the reverse geocoder and I get the first two
processed correctly but after that I get an error and the third one isn't processed. I thought that if I used the SetTimeout function either in the for
loop or in one of the lower level functions this would delay the requests enough to be able to process all 3 addresses but no matter where I
place the SetTimeout function it continues to get the 429 error. When I log the time to the console, I can see that the three calls to the
reverse geocoder happen at the same time. Can anyone suggest where I can place the timeout to slow down the requests enough?
Thanks (last attempted version of code below)
for (let i = 0; i < mapMarkers.length; i++){
// use a reverse geocode function to build a display address for each of the co-ordinates chosen
SetTimeout(reverseGeocode(mapMarkers[i].getLatLng()), 1000);
};
function reverseGeocode(coords){
var today = new Date();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
console.log("Into reverse geocoder " + time);
let REVERSEURL = `https:....`
let TOKEN = '.....'
let url = `${REVERSEURL}key=${TOKEN}&lat=${coords.lat}&lon=${coords.lng}`;
//do a reverse geocoding call
getData(url, coords);
}
async function getData(url,coords) {
try {
const data = await getRequest(url);
// create a display address with the first three elements containing something in the address dictionary
let address = createAddressString(data.address, 3) +
" ( " + coords.lat.toFixed(5) + ", " + coords.lng.toFixed(5) + " ) ";
// Insert a div containing the address just built
$("#addresses-container").append("<div class='request-page'>"+address+'</div>');
} catch(e) {
console.log(e);
}
}
async function getRequest(url) {
const res = await fetch(url);
if (res.ok) {
return res.json();
} else {
throw new Error("Bad response");
}
}
Your current logic is invoking the reverseGeocode() method immediately, as you pass the response from that function call to the timeout. You need to provide a function reference instead.
Even if you correct that issue, then you would instead delay all the requests by 1 second, but they would still get fired at the same time.
To stagger them you can use the index of the iteration to multiply the delay. For example, the following logic will fire 1 request every 250ms. This delay can be amended depending on what the rate limit is of your API provider. Also note that SetTimeout() needs to be setTimeout()
for (let i = 0; i < mapMarkers.length; i++) {
setTimeout(() => reverseGeocode(mapMarkers[i].getLatLng()), 250 * i);
}
Aside from the problem, it would be worth checking if the API can accept multiple lookups in a single request, which will alleviate the issue. Failing that, I'd suggest finding an alternative provider which allows more than 3 requests per N period.

Sending a message in smaller chunks

I am using DiscordJS and their API has a character limit and will reject message if limit is exceeded.
Through fetchData()I am successfully building assignedPrint, which is an array of messages that I would like to send over the API.
So I already have the array ready to go but I am also using an auto update feature (courtesy of WOK) where every set amount of time, array would be flushed refilled with fresh data and sent over again for editing the original message through the message.edit() method.
It works just fine but I am forseeing that my array might get bigger over time and sending a single message may break things because of the API max character limit.
const getText = () => {
return assignedPrint
+ greengo + 'Updating in ' + counter + 's...' + greenstop;
};
const updateCounter = async (message) => {
message.edit(getText());
counter -= seconds;
if (counter <= 0) {
counter = startingCounter;
// emptying the array before fetching again for the message edit
assignedPrint = [];
await fetchData();
}
setTimeout(() => {
updateCounter(message);
}, 1000 * seconds);
};
module.exports = async (bot) => {
await fetchData();
const message = await bot.channels.cache.get(tgt).send(getText());
updateCounter(message);
};
As you can see, the hurdle is that getText()includes everything.
I tried sending one element as a time using for (cont e of assignedPrint) and it worked but how can I edit every message upon refreshing data, knowing that new data may be added or some already sent data could be removed...
The easiest of course is to do it in one single message but again, it may hit the quota and cause a crash.
Thanks.

How to make dialogflow going while waiting for a process that takes more than the time limit?

I have created an API that takes user input and process it. However, the process takes more than 5 seconds (dialogflow limit).
How can I continue with other processes until this certain process is finished?
Or is it possible to return to the user any messages like "Please hold on a bit..." so it can restart the time.
var message = "hi"; //test purpose
async function GetCertain_info(agent) {
await uiFx();
agent.add("Here are the information on " + message);
}
async function uiFx() {
var {
ui
} = require('./uia.js');
return new Promise(function(resolve, reject) {
ui().then((msg) => {
console.log("Destination Message : " + msg)
message = msg;
resolve(message);
}).catch((msg) => {
console.log(msg)
message = msg;
reject(message);
})
});
}
Appreciate your help
Yes, it is possible to return to the user any messages like "Please hold on a bitā€¦" by setting up a FollowupEvent.
You can extend the 5 seconds Intent limit up to 15 seconds by setting up multiple follow-up events. Currently, you can only set up 3 follow-up events one after another (which can extend the timeout up to 15 seconds).
Here's an example of how you can do it in the fulfillment:
function function1(agent){
//This function handles your intent fulfillment
//you can initialize your db query here.
//When data is found, store it in a separate table for quick search
//get current date
var currentTime = new Date().getTime();
while (currentTime + 4500 >= new Date().getTime()) {
/*waits for 4.5 seconds
You can check every second if data is available in the database
if not, call the next follow up event and do the
same while loop in the next follow-up event
(up to 3 follow up events)
*/
/*
if(date.found){
agent.add('your data here');//Returns response to user
}
*/
}
//add a follow-up event
agent.setFollowupEvent('customEvent1');
//add a default response (in case there's a problem with the follow-up event)
agent.add("This is function1");
}
let intentMap = new Map();
intentMap.set('Your intent name here', function1);;
agent.handleRequest(intentMap);

HTML: How to get a calculation result before executing a function

I have a script that I am trying to execute. I need the script to, on the press of the submit button:
fetch values from Google firestore
Use those values in a calculation
use the result of this calculation to write back to the firestore database
I have put console.log("eff[1,2,3]" + result of calculation) to help trace in the console log the order of execution. It first displays eff3 then eff1 then eff2 so this means that the value submitted to firestore is zero (as it is before the calculation) The calculation does calculate the correct answer it just doesn't submit the answer to the database.
How do I change my code to have it execute in the right order?
// Triggered when the send new message form is submitted.
function onMessageFormSubmit(e) {
e.preventDefault();
//Calculate efficiency
var preodo = 0
var efficiency = 0
firebase.firestore().collection('Vehicle').doc(reg.value).collection('Refuels').orderBy('Date', 'desc').limit(1)
.onSnapshot(function(snapshot) {
snapshot.docChanges().forEach(function(change) {
preodo = change.doc.data().Odometer
efficiency = (odo.value - preodo) / amount.value
efficiency = efficiency.toFixed(4);
console.log("eff1:" + efficiency)
});
console.log("eff2:" + efficiency)
});
//Save refuel transaction with all details form the form as well as the calculated efficiency
console.log("eff3:" + efficiency)
saveMessage(reg.value, odo.value, date.value, amount.value, price.value, rebate.value, efficiency).then(function() {
window.alert('Refuel submitted!')
// Clear message text field and re-enable the SEND button.
resetMaterialTextfield();
toggleButton();
});
}
Firestore operations are asynchronous, so you'll need to handle them with a promise. Do exactly what you did with saveMessage(), but with returning a value
Also, use .get() instead of .onSnapshot(), as the latter will continue to listen to changes made to the referenced document, which you don't need because you do this operations when a button is clicked.
function get_efficiency(regValue) {
efficiency = -1
// return a Promise which will return the efficiency after all computations are done
return firebase.firestore().collection('Vehicle')
.doc(reg.value)
.collection('Refuels')
.orderBy('Date', 'desc')
.limit(1)
.get()
.then(function (querySnapshots) { // this will execute after all documents are fetched from the db
querySnapshots.forEach(function(doc) {
preodo = doc.data().Odometer
efficiency = (odo.value - preodo) / amount.value
efficiency = efficiency.toFixed(4);
console.log("eff1:" + efficiency)
});
console.log("eff2:" + efficiency)
return efficiency
})
}
// Triggered when the send new message form is submitted.
function onMessageFormSubmit(e) {
e.preventDefault();
//Calculate efficiency
var preodo = 0
var efficiency = 0
get_efficiency(reg.value)
.then(function (efficiency) { // this will execute after you computed the efficiency
// efficiency param is the one returned from the PROMISE from get_efficiency()
//Save refuel transaction with all details form the form as well as the calculated efficiency
console.log("eff3:" + efficiency)
saveMessage(reg.value, odo.value, date.value, amount.value, price.value, rebate.value, efficiency).then(function() {
window.alert('Refuel submitted!')
// Clear message text field and re-enable the SEND button.
resetMaterialTextfield();
toggleButton();
});
})
}
By using #Professor Abronsius' suggestion, I put my code in a Promise
// Triggered when the send new message form is submitted.
function onMessageFormSubmit(e) {
e.preventDefault();
let myPromise = new Promise(function(myResolve, myReject) {
//Calculate efficiency
var preodo = 0
var efficiency = 0
firebase.firestore().collection('Vehicle').doc(reg.value).collection('Refuels').orderBy('Date', 'desc').limit(1)
.onSnapshot(function(snapshot) {
snapshot.docChanges().forEach(function(change) {
preodo = change.doc.data().Odometer
efficiency = (odo.value - preodo) / amount.value
efficiency = efficiency.toFixed(4);
console.log("eff1:" + efficiency)
});
console.log("eff2:" + efficiency)
myResolve(efficiency); // when successful
myReject("Not able to calculate efficiency"); // when error
});
});
// "Consuming Code" (Must wait for a fulfilled Promise)
myPromise.then(
function(efficiency) {
//Save refuel transaction with all details form the form as well as the calculated efficiency
console.log("eff3:" + efficiency)
saveMessage(reg.value, odo.value, date.value, amount.value, price.value, rebate.value, efficiency).then(function() {
window.alert('Refuel submitted!')
// Clear message text field and re-enable the SEND button.
resetMaterialTextfield();
toggleButton();
});
},
function(error) {
window.alert(error)
}
);
}
This works... It submits to the firestore only once but... it prints to the console log as follows:
eff1, eff2, eff3, eff1, eff1, eff2.
I'm now curious as to why it repeats itself? It doesn't really matter that it repeats this because it doesn't affect the database or the user interface but I am curious.

Meteor setInterval and query

I want to have a function that checks every 5 seconds for all entries in database for some value is false and if finds then checks some logic condition and changes the value to true if the logic condition is met.
My function works well until I have something with isItReady: false in my collection. When I don't have, it obviously doesn't find anything and I start getting errors.
How should I do this correctly? I don't want to stop my interval because maybe something will be entered into the collection soon and then my inverval is stopped?
How can I do something like this:
If nothing matches my search criterea - productDate = Products.findOne({isItReady: false}); the interval is stopped and as soon as something new gets inserted I will start the inverval again?
var logicCheck = Meteor.setInterval( function () {
productDate = Products.findOne({isItReady: false}); //query to find all entries with isItReady: false
var timeNow = Date();
var timeCreated = productDate.startOfCountdown;
timeCreated = timeCreated.toString(); //converts timeCreated from object to String(in Mongo its a object)
var productId = productDate._id;
console.log(typeof timeNow) //string
console.log(typeof timeCreated) //string
console.log(timeNow + "timeNow")
console.log(timeCreated + "timeCreated")
if (timeCreated <= timeNow) {
console.log("check") //this works well
Products.update({_id: productId}, {$set: {isItReady: true}}, function(error, result) {
console.log(productId) //all good
if (error){
console.log(error.reason) //check the error
} else{
console.log("File with the id: " + result + " just get update")
}
});
}
}, 5000);
Your approach of polling MongoDB every 5 seconds is very non-Meteoric. You'd be much better off creating a Tracker.autorun function to instantly react to any product that has isItReady == false
For example:
Tracker.autorun(function(){
var notReadyProducts = Products.find({ isItReady: false });
notReadyProducts.forEach(function(p){
if ( your logic ... ) Products.update({ _id: p._id },{ $set: { isItReady: true }});
});
});
This assumes by the way that you're publishing (on the server) and subscribing to (on the client) a set of Products that is going to include these not ready products.
With this pattern no code will be running 99.99% of the time and then at the precise moment that a product is made not ready this code will kick in.
You might want to take a look at this video to learn more about reactive programming and how it completely changes the way you approach common problems. There are many other resources available as well.

Categories