How to cache asynchronous result for reuse - javascript

I have a function handle_response that has 2 outcomes directed using an if statement. Here is the code:
function handle_response(response) {
if (response["unlocked"]) {
activate_slider();
// ideally reset_slider() comes here
} else {
error(response["message"]);
reset_slider();
}
}
function activate_slider() {
setLocked(false);
setTimeout(function () {
setLocked(true);
reset_slider();
}, 3000)
}
I'd like to move reset_slider() out from activate_slider() and into the body of the if statement, so that it becomes analogous to the reset_slider() in the else statement which reads better. But I'm unsure how to do so because TWO functions depend on setTimeout (i.e., if there were just reset_slider(), then I'd just put in the if body setTimeout( reset_slider(), 3000).
Also suggestions on better abstractions welcome!

You're executing the setTimeOut method, so, an async process will be executed in the future.
Hence, the only way to extract the call of reset_slider is trough callback functions:
function handle_response(response) {
if (response["unlocked"]) {
activate_slider(reset_slider); // ideally reset_slider() comes here
} else {
error(response["message"]);
reset_slider();
}
}
function activate_slider(cb) {
setLocked(false);
setTimeout(function () {
setLocked(true);
cb();
}, 3000)
}

Related

Javascript Callback Not Working as Intended

I am trying to ensure that checkteamexists only executes after checklogin has executed. However, checkteamexists still functions after checklogin.
You can imagine teamhome1_message and teamhome2_message as alert dialogs. They pop a message up and didn't return anything.
function pushhistory(url, callback) {
history.push(url);
callback();
}
function checklogin(callback) {
if (!state.user.authenticated) {
pushhistory("/accounts/login", function() {
teamhome2_message()
});
}
callback();
}
function checkteamexists(teamname) {
if (teamname.toString().toLowerCase() == "team1") {
teamid = 1;
}
else {
pushhistory("/", function() {
teamhome1_message()
});
}
}
useEffect(() => {
checklogin(function() {
checkteamexists(teamname);
})
}, []);
checklogin worked because the URL became /accounts/login and prompted teamhome2_message. However, teamhome1_message still appeared even though I don't want it to.
I tried specifying a callback in the useEffect hook (which is specific to React) but the callback didn't seem to work either. Can anyone please point out the problem?
Thanks in advance.
#p2pdops had the right solution: put the callback function in the else block.

How to run setTimeOut once in repeatedly running function?

function setPositionFromEngine is running multiple times. I need to run setTimeout once in this function and then start anotherFunction after set timeout is done
setPositionFromEngine(pos) {
setTimeout(() => {
this.setPos(pos);
}, 2000);
anotherFunction() {...}
}
If anotherFunction needs to run after the specified timeout then move it inside of setTimeout. Then if you need to ensure that setTimeout only runs once, you need to add a condition, such as:
// Somewhere outside of setPositionFromEngine
var didSetPosition = false;
setPositionFromEngine (pos) {
if (!didSetPosition) {
setTimeout(() => {
this.setPos(pos);
didSetPosition = true; // <-- This is key
anotherFunction();
}, 2000);
}
}

NodeJS callback in a for-loop value is the same

I was cleaning up my code and ran into a little trouble with callbacks, specifically getting the correct values to output when a callback is called. Could some explain to me why the following code spits out something that I'm not expecting and a possible solution without having to put in another parameter of i to the run() function, or is passing in i to know my index upon calling the callback the only way to do this?
for (var i in dls) {
run(dls[i][0], dls[i][1], function(isTrue){
if (isTrue) {
// Do true stuff here
} else {
console.log("Value is: " + dls[i][3])
}
});
}
Calling run() actually has the correct inputs inside, but upon that function calling the callback and going into the else statement, dls[i][3] spits out the same value i times.
I've tried putting different scopes around (run()) and such but to no avail and can't seem to wrap my head around this.
Thanks
EDIT:
If I wanted to split it up into a separate function, how would I do it?
var run = function(cb){
setTimeout(function() {
cb(false)
}, 3000);
}
for (var i in dls) {
run(dls[i][0], dls[i][1], (function(index) {
return extraction
})(i));
}
function extraction(isTrue){
if (isTrue) {
// stuff
} else {
console.log("Nothing changed in " + dls[i][3])
}
}
Here dls[i][3] is still incorrect and prints the same value 3 times.
You have fallen into the traditional "loop trap"
When it comes time for your callback to run i is now a different value.
What you can do is cache that value in another wrapper function:
for (var i in dls) {
run(dls[i][0], dls[i][1], (function (currentIndex) {
return function(isTrue){
if (isTrue) {
// Do true stuff here
} else {
console.log("Value is: " + dls[currentIndex][3])
}
};
})(i));
}
In regards to the edit / second question, assuming this is what you wanted to do:
// note that I changed the function signature of `run`
var run = function(val1, val2, cb) {
setTimeout(function() {
cb(false);
}, 3000);
};
// note the `wrapper` here
for (var i in dls) {
run(dls[i][0], dls[i][1], wrapper(i));
}
// this is the same as what the IIFE is doing,
// just with an external function instead
function wrapper(scopedIndex) {
// return a function to be used as the callback for `run`
return function extraction(isTrue) {
if (isTrue) {
// stuff
}
else {
// use the scoped index here
console.log("Nothing changed in " + dls[scopedIndex][3]);
}
}
}
Take a look at function makeExitCallback(i) in the other linked question, as well. It directly relates to what's going on here.
You should post what's in dls as well, just to make it easier to run your snippets locally.

Returning from a parent function from inside a child function - Javascript

I'm relatively new to coding in JavaScript, and I've came across a problem. I like to nest functions to keep things orderly, but how would I exit from a parent function from inside a child function?
example:
function foo1() {
function foo2() {
//return foo1() and foo2()?
}
foo2();
}
See update under the fold
You can't. You can only return from the child function, and then return from the parent function.
I should note that in your example, nothing ever calls foo2 (As of your edit, something does). Let's look at a more real example (and one that comes up a lot): Let's say we want know if an array contains an entry matching some criterion. A first stab might be:
function doesArrayContainEntry(someArray) {
someArray.forEach(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does <-- This is wrong
}
});
return false; // No it doesn't
}
You can't directly do that. Instead, you have to return from your anonymous iterator function in a way to stop the forEach loop. Since forEach doesn't offer a way to do that, you use some, which does:
function doesArrayContainEntry(someArray) {
return someArray.some(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does
}
});
}
some returns true (and stops looping) if any call to the iterator function returns true; it returns false if no call to the iterator returned true.
Again, that's just one common example.
You've referred to setInterval below, which tells me that you're almost certainly doing this in a browser environment.
If so, your play function almost certainly has already returned by the time you want to do what you're talking about, assuming the game has any interaction with the user other than alert and confirm. This is because of the asynchronous nature of the environment.
For example:
function play() {
var health = 100;
function handleEvent() {
// Handle the event, impacting health
if (health < 0 {
// Here's where you probably wanted to call die()
}
}
hookUpSomeEvent(handleEvent);
}
The thing is, that play will run and return almost immediately. Then the browser waits for the event you hooked up to occur, and if it does, it triggers the code in handleEvent. But play has long-since returned.
Make a note whether the parent function should also return.
function foo1() {
bool shouldReturn = false;
function foo2() {
shouldReturn = true; // put some logic here to tell if foo1() should also return
return;
}
if (shouldReturn) {
return;
} else {
// continue
}
}
It only says that you can't return the parent function in the child function, but we can do a callback and make it happen.
function foo1(cb = () => null) {
function foo2() {
cb();
}
foo2();
}
foo1(() => {
// do something
});
We can use Promises for this:
const fun1 = async () => {
const shouldReturn = await new Promise((resolve, reject) => {
// in-game logic...
resolve(true)
})
if(shouldReturn) return;
}
if you wanna return from the parent function, then just resolve with true
Based on your comment, something like this might work as a main game loop.
function play() {
var stillPlaying = true;
while(stillPlaying) {
... play game ...
stillPlaying = false; // set this when some condition has determined you are done
}
}

Prevent JS code to run, before function has ended

I can't wrap my head around this one?
WHY IS IT THAT MY:
.each loop runs right through even though I'm stalling things inside by 1000ms per loop?
The problem is that the window.location.href command runs TO EARLY before the setTimeout has finished? Same for the stopload() function which is also ended to early? I have seen something about recursive setTimeout functions is that what is needed here and how do I implement that?
function shop(clickedButton)
{
var buttonvalue = $(clickedButton).val();
startLoad();
pdel = 1000;
$("input:submit[value='buy']").each(function(index)
{
if(index != 1)
{
$("#backgroundPopup").text(index);
var submithing = this;
setTimeout(function(){ clicksubmitbutton(submithing); },pdel);
pdel += 1000;
}
});
stopLoad();
if(buttonvalue == "1")
{
window.scrollTo(0,0);
}
else
{
window.location.href = 'http://my.url';
}
}
Javascript has no notion of a sleep, or stall. Execution continues right past a setTimeout call. This function simply schedules a function to be run after the given number of milliseconds.
In order to get your desired behavior, you need to call the next iteration in the setTimeout callback function.
Something like:
function submitTheThing(index) {
if (done) { //some logic
return;
}
// do something
setTimeout(function() { submitTheThing(index+1); }, 1000);
}

Categories