while loop not behaving as expected in JavaScript - javascript

First of all, it must be said that this may be a setState issue as I have been having some trouble with that but I cannot work out why this while loop wont work.
code:
dealerTwisted = () => {
console.log(this.state.dealersOverallTotal, 'total on entry');
let looping = true;
while(looping){
console.log(this.state.dealersOverallTotal,'same');
if(this.state.dealersOverallTotal < 17){
this.deal2Dealer();
let dealersDeck = this.state.dealersDeck;
let newDealersDeckTotal = [];
for (var i=0; i < dealersDeck.length; i++){
newDealersDeckTotal.push(dealersDeck[i].rankValue)
}
let total = newDealersDeckTotal.reduce(function(a, b) {
return a + b;
},
0);
console.log(total, 'tot');
this.setState({ dealersOverallTotal: total });
}
else{
console.log(this.state.dealersOverallTotal, 'logging as greater than 17');
if(this.state.playersOverallTotal > this.state.dealersOverallTotal){
console.log('player wins!');
looping = false;
}
else if (this.state.playersOverallTotal > this.state.dealersOverallTotal){
console.log('its a tie!');
looping = false;
}
else {
console.log('dealer wins!');
looping = false;
}
}
}
};
The way I think it is and the way I want it to work is as follows:
the function gets called, the loop is entered, you enter the if block if this.state.dealersOverallTotal is less than 17. that total then gets updated and then you begin the loop again. you continue to enter the if block until you get to a number higher than 17. once you do you enter the else block and then whichever condition you hit in there, you break out.
interesting I have just debugged and found that this line (the fist console log) console.log(this.state.dealersOverallTotal, 'total on entry'); gives me the same number every time, i.e. not updating once inside. can anyone see why?

React's setState is asynchronous, so your this.state.dealersOverallTotal never reaches 17.
setState in reactjs is Async or Sync

Related

SOLVED: Updating async functions between modules

I have been working on a simple loading animation for backend node.js projects and I ran into a problem after trying to modularize it (so that I can just require() it in other projects)
When run, this loading bar creates a spinner out of braille characters in the console and tries to take three inputs (which used to be global variables before I tried to encapsulate the function, however, when that didn't work I added them to the exported variables in module.exports):
totalSteps: the total # of steps the given function has.
currentStep: the current step the function is on.
statusMessage: the message to display while the spinner loads.
every 100ms, the spinner function updates the frame on the animation which includes the spinner, a percentage of complete tasks, the fraction of done vs not done steps, the message, and three animated trailing dots.
for some reason I can not get this function to work now that I have put it in its own module. It does not wait for the provided function to end before it prints its "Done!" message. however, the variables do seem to be shared between the two scripts, as the final "Done!" message includes the message, and the step variables in it.
Here's my code so far for the loading.js file:
module.exports = {
currentStep : 0,
totalSteps : 0,
statusMessage : '',
spinner : async function (func) {
// declare array of frames for the animation
const spinnerAnimation = ['[⠋]', '[⠙]', '[⠹]', '[⠸]', '[⠼]', '[⠴]', '[⠦]', '[⠧]', '[⠇]', '[⠏]'];
const dots = [' ', ' ', ' ', '.', '.', '.', '.. ', '.. ', '.. ', '...', '...', '...'];
let spinnerCounter = 0;
let dotCounter = 0;
// overwrite the line with the new frame every 100ms
const intervalId = setInterval(() => {
spinnerCounter = (spinnerCounter + 1) % spinnerAnimation.length;
dotCounter = (dotCounter + 1) % dots.length;
if (this.statusMessage == '') this.statusMessage = 'Loading';
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
// if the variables used for calculating the % value are in the correct range
// show the % and the # of steps completed out of the total
process.stdout.write(`\r\x1b[32m\x1b[?25l${spinnerAnimation[spinnerCounter]} ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps} complete)\x1b[0m - ${this.statusMessage}${dots[dotCounter]} `);
} else {
// if not, just dont show them. that way if it fails, the animation still shows but you dont get any weird errors (or NaN/undefined placeholders)
process.stdout.write(`\r\x1b[32m\x1b[?25l${spinnerAnimation[spinnerCounter]}\x1b[0m - ${this.statusMessage}${dots[dotCounter]} `);
}
}, 100);
let error = null;
try {
// await some async operation
await func();
} catch (err) {
error = err
} finally {
clearInterval(intervalId);
if (this.statusMessage == '') this.statusMessage = 'Loading';
if (error) {
// if an error was caught, mark the bar as "Failed!" and post the message
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
process.stdout.write(`\r\x1b[31m[✓] ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps})\x1b[0m - ${this.statusMessage}... >> FAILED!\x1b[?25h \n`);
} else {
process.stdout.write(`\r\x1b[32m[✓]\x1b[0m - ${this.statusMessage}... >> FAILED!\x1b[?25h \n`);
}
console.error(`\x1b[31m └-${error}\x1b[?25h\x1b[0m`);
this.currentStep = 0;
this.totalSteps = 0;
this.statusMessage = '';
return error;
} else {
// if there was no error, stop the bar and return to the script
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
process.stdout.write(`\r\x1b[32m[✓] ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps})\x1b[0m - ${this.statusMessage}... >> Done!\x1b[?25h \n`);
} else {
process.stdout.write(`\r\x1b[32m[✓]\x1b[0m - ${this.statusMessage}... >> Done!\x1b[?25h \n`);
}
this.currentStep = 0;
this.totalSteps = 0;
this.statusMessage = '';
return;
}
}
}
}
And for the index.js:
const loading = require('./loading.js');
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function something() {
loading.totalSteps = 3;
loading.statusMessage = "doing something";
sleep(20000);
loading.currentStep = 1;
loading.statusMessage = "working harder";
sleep(2000);
loading.currentStep = 2;
loading.statusMessage = "almost done";
sleep(2000);
loading.currentStep = 3;
}
async function main() {
await loading.spinner(something);
}
main();
The output for index.js script is as follows:
[✓] 100% (Step: 3/3) - almost done... >> Done!
This is shown almost as soon as it is run, without waiting for the something() function to finish waiting, however the program does not close until the something() function finishes waiting. I don't understand why the spinner never shows up though. any ideas?
on a side note, Is there any way to make it so that the entire line is overwritten every frame? for example, if the statusMessage text is shorter than the frame before it, the end of the last frame persists into the second one because the rest was not overwritten with new text. My hacky solution to this was to just append a lot of spaces to the end of each line so that so long as the message text is short you wont see it but I realize it wont fix the problem, how does one measure the length of the last line and append the correct number of spaces to the end of the new line or clear the line entirely before writing the next one.
I understand this is probably a lot of stuff to go through so I apologize. Thank you in advance! If there's any other information I should provide please ask!

How to execute code in order in a while loop in Javascript?

I'm trying to run some JavaScript code on Postman, but I can't find a way for it to run in the order that I need. This is what I'm trying to do:
Retrieve the API response and verify if the "pending" array contains any item
If id does, I'll save the id of the record (orderId) in an environment variable to use in my actual request
At this point I would set found = true and break the loop when it leaves the setTimout function
Note: I created the function to introduce 400ms delay between the attempts, as it will allow the pending array to be populated
var found = false;
var counter = 0;
while (counter < 10) {
setTimeout(async () => {
var size = await response.json().pending.length;
if (size > 0) {
var orderId = response.json().pending[0].orderId;
pm.environment.set("current_order", orderId);
found = true;
}
}, [400]);
console.log(found);
if (found) { break; }
counter++;
}
My problem is that the part that is outside the setTimeout function executes first, so it will never satisfy the condition "If (found)". It always executes the code 10 times, even if the record is found in the first attempt.
My question is: How can I write it in order to check if the record was found after each attempt and break from the loop if positive?
Thanks!
As suggested above, you might be able to solve this issue with a simpler recursive function. An example on how it would look:
var found = false;
var size = 0;
async function checkResponse() {
var size = await response.json().pending.length;
if (size > 0) {
var orderId = await response.json().pending[0].orderId;
pm.environment.set('current_order', orderId);
found = true;
}
if (!found) {
await checkResponse();
}
}
await checkResponse();
Here is a synchronous test for the logic above:
var found = false;
// var size = 0;
var size = -5;
function checkResponse() {
// var size = await response.json().pending.length;
size += 1;
console.log(size);
if (size > 0) {
// var orderId = response.json().pending[0].orderId;
// pm.environment.set('current_order', orderId);
console.log("pm.environment.set('current_order', orderId);");
found = true;
}
console.log('END?');
console.log(found);
if (!found) {
checkResponse();
console.log('checkResponse();');
}
}
checkResponse();
Output:
-4
END?
false
-3
END?
false
-2
END?
false
-1
END?
false
0
END?
false
1
pm.environment.set('current_order', orderId);
END?
true
checkResponse();
checkResponse();
checkResponse();
checkResponse();
checkResponse();

how to search and compare the items in the array gotten from the output of a loop after the loop is complete (javascript)

please im having a bit of a problem with my javascript code.... im working on a reminder app in Cordova and im using Katzer notification plugin... now i am coding in javascript and im having a little challenge. im trying to achieve a feature whereby , when a user tries to add a reminder that already exists, it will throw an error... and if the reminder dosen't exist, it will add it to the list of reminders.... im using as javascript loop for this... heres my code
function checkifReminderExists(){
cordova.plugins.notification.local.getAll(function (notifications) {
var allRemInfo = "";
var newAllRemInfo = "";
// using while loop
var count = 0;
while (count < notifications.length) {
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text ;
if(allRemInfo.indexOf(""+checkedBoxes+"") == true)
{
alert("sorry you cant add a reminder that already exist...");
} else {
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}
});
count++
continue;
}
});
}
/* the above did not work so i tried using for loop to achieve this
function checkifReminderExists(){
cordova.plugins.notification.local.getAll(function (notifications) {
var allRemInfo = "";
var newAllRemInfo = "";
var count;
for(count = 0; count < notifications.length; count++)
{
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text + ", " ;
newAllRemInfo = new Array(""+allRemInfo+"");
if(newAllRemInfo.indexOf(""+checkedBoxes+"") == true)
{
alert("sorry you cant add a reminder that already exist...");
} else
{
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}
});
}
});
}
I tried both methods(for and while loop) above and none of them gave me my result... instead the "if()else" test will run separately on each of the loop...the disadvantage of this, is that when a test runs on the first item in the list of reminders, the setLecReminders(); function runs irrespective of if the subsequent test are true or false... i want a solution whereby the loop runs completely first and all items on the list are outputted into an array and then i can use a if()else test on all members of the array simultaneously. Please pardon my long question... thanks in advance
I would suggest setting a boolean inside of your loop like this:
var reminderExists = false;
while (count < notifications.length)
{
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text;
if (allRemInfo.indexOf(""+checkedBoxes+"") == true)
{
reminderExists = true;
}
count++;
}
Then you can check the boolean outside of the loop and show your alerts based on that result:
if (reminderExists) {
alert("sorry you cant add a reminder that already exist...");
} else {
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}

Reset timeout on event with RxJS

I'm experimenting with RxJS (with the JQuery extension) and I'm trying to solve the following use case:
Given that I have two buttons (A & B) I'd like to print a message if a certain "secret combination" is clicked within a given timeframe. For example the "secret combination" could be to click "ABBABA" within 5 seconds. If the combination is not entered within 5 seconds a timeout message should be displayed. This is what I currently have:
var secretCombination = "ABBABA";
var buttonA = $("#button-a").clickAsObservable().map(function () { return "A"; });
var buttonB = $("#button-b").clickAsObservable().map(function () { return "B"; });
var bothButtons = Rx.Observable.merge(buttonA, buttonB);
var outputDiv = $("#output");
bothButtons.do(function (buttonName) {
outputDiv.append(buttonName);
}).bufferWithTimeOrCount(5000, 6).map(function (combination) {
return combination.reduce(function (combination, buttonName) {
return combination + buttonName;
}, "");
}).map(function (combination) {
return combination === secretCombination;
}).subscribe(function (successfulCombination) {
if (successfulCombination) {
outputDiv.html("Combination unlocked!");
} else {
outputDiv.html("You're not fast enough, try again!");
}
});
While this works fairly well it's not exactly what I want. I need the bufferWithTimeOrCount to be reset when button A is pressed for the first time in a new timeframe. What I'm looking for is that as soon as the secret combination is pressed (ABBABA) I'd like "Combination unlocked!" to be shown (I don't want to wait for the time window to be expired).
Throttle is the typical operator for the delaying with reactive resetting effect you want.
Here's how you can use throttle in combination with scan to gather the combination inputted before the 5 seconds of silence:
var evaluationStream = bothButtons
.merge(bothButtons.throttle(5000).map(function(){return "reset";})) // (2) and (3)
.scan(function(acc, x) { // (1)
if (x === "reset") return "";
var newAcc = acc + x;
if (newAcc.length > secretCombination.length) {
return newAcc.substr(newAcc.length - secretCombination.length);
}
else {
return newAcc;
}
})
.map(function(combination) {
return combination === secretCombination;
});
var wrongStream = evaluationStream
.throttle(5000)
.filter(function(result) { return result === false; });
var correctStream = evaluationStream
.filter(function(result) { return result === true; });
wrongStream.subscribe(function() {
outputDiv.html("Too slow or wrong!");
});
correctStream.subscribe(function() {
outputDiv.html("Combination unlocked!");
});
(1) We scan to concatenate the input characters. (2) Throttle waits for 5 seconds of event silence and emits the last event before that silence. In other words, it's similar to delay, except it resets the inner timer when a new event is seen on the source Observable. We need to reset the scan's concatenation (1), so we just map the same throttled Observable to "reset" flags (3), which the scan will interpret as clearing the accumulator (acc).
And here's a JSFiddle.

How to get my loop to start from the beginning after error detected

I am trying to get my loop to restart when it comes across an user input error. I need it to restart at the very beginning and not just the last question.
So below when it says validImput = false this is where I am trying to get it to restart.
{
var validInput = true;
var start = confirm('Add item to shoping cart');
if (start == true) {
// ask first question
var orderProductCodeArr = parseInt(prompt('Enter input: '), 10);
if (isNaN(orderProductCodeArr)) {
alert("input is not a valid number");
validImput = false
} else if (orderProductCodeArr < 0 || orderProductCodeArr >= PRODUCT_LIST.length) {
alert("code does not match any item");
validInput = false;
}
// ask second question
else if(validInput == true) {
var item = PRODUCT_LIST[orderProductCodeArr];
alert("item is: " + item);
}
// get quantity input
var quanityArr = parseInt (prompt('Enter quality amount'),10);
if (isNaN(quanityArr)) {
alert("input is not a valid number");
validInput = false;
}
} else {
document.writeln('still to come')
}
}
The usual method of starting something over is some sort of loop construct, often using while like this:
while (true) {
// your loop code here
// you can use break; to break out of the while loop
// anywhere to stop repeating
// you can use continue; to jump to the next iteration immediately
}
Or, sometimes you use a loop condition like this:
var doAgain = true;
while (doAgain) {
// within the loop, you set doAgain to false when you are done
// and don't want to repeat the loop again
}
try
function test()
{
for(var s=0;s<5;s++)
{
try
{
//body of the for loop
}catch(e){s=0;}
}
}

Categories

Resources