I have a render() function, and inside of it there's this sleep() function:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
(Using this inside an async function)
On the first run, everything is fine, but when I click on the new game button (which calls render()) a new Promise is created, but the previous one is still there too. So the sleep() counts time double.
I've read the MDN documentation and some stackoverflow questions, but didn't find the answer for this.
Can someone explain me how Promise actually works, and what have I done wrong?
EDIT:
When the game is finished and click on New Game button, everything seems fine except timer, which is counts seconds twice.
The code:
const render = () => {
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const timerOnePlayer = async () => {
timer.innerHTML = 0;
timerText.innerHTML = "Eltelt idő";
while (!gameFinished) {
await sleep(1000);
timer.innerHTML = Number(timer.innerHTML) + 1;
}
}
/** Some other irrelevant code*/
}
const resetValues = () => {
/** variables set to zero or empty */
render();
}
btnStart.addEventListener("click",render);
newGame.addEventListener("click",resetValues);
When I started this project didn't really know how Promise or setIntervall works. But with the comments, I figured out what's wrong, removed sleep() from the project and replaced with just setIntervall. Now everything works fine, so thanks again!
const timerOnePlayer = () => {
timer.innerHTML = 0;
timerText.innerHTML = "Eltelt idő";
onePlayerInterval = setInterval(() => {
timer.innerHTML = Number(timer.innerHTML) + 1;
if (gameFinished) clearInterval(onePlayerInterval);
}, 1000);
};
Related
I have a database setup with NodeJS and want to wait until certain table is created before start to create any others. This method tableExists resolves with the status being either true/false, but I want it to wait until it's true only.
const checkTableExists = async () => {
const exists = await queryInterface.tableExists('Subjects');
return exists;
}
How can I force a wait until checkTableExists returns true?
Using setTimeout:
const CHECK_INTERVAL = 200; // every 200ms
const checkTableExists = async () => {
const exists = await queryInterface.tableExists('Subjects');
if (!exists) {
return new Promise((resolve, reject) => {
setTimeout(() => checkTableExists().then(resolve).catch(reject), CHECK_INTERVAL);
});
}
return exists;
}
The solution to something like this is not to keep on waiting. There are other issues that may cause the table not to be created. You may want to adjust the above code to stop checking after it has checked for set number of times, or a duration has passed. Use something reasonable, depending on the environment where your db is running.
Add a delay and repeat:
// Utility function
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const checkTableExists = async () => {
while (true) {
const exists = await queryInterface.tableExists('Subjects');
if (exists) return true;
await delay(10000); // Wait 10 seconds before trying again.
}
}
Although this resolves the promise with true, it is actually is not necessary to return a boolean, as the resolving promise is enough as a signal that the table now exists -- true is the only possible outcome when the promise resolves.
So i am making some kind of game, where a player has some powerups. After the players turn is over, there should be a 5 sec timeout in the server, where no code is executed, and then the turn should be passed after the time. However if the client clicks in one of the powerups, the server should stop the 5 second timeout and start executing again. How do i implement this?
Currently i am using,
await new Promise(r => setTimeout(r, 5000))
which stops and waits for the timeout to end, but how can I stop the timeout when the client selects a powerUp? How do we clear Promise based timeout?
To be short what i want to do is:
server side code
function doSomething(){
if(playerHasPowerUps) await new Promise(r => setTimeout(r, 5000))
//do other things
}
in client side in this period if the player clicks on a powerup, it
informs the server about the event and the server is meant to stop
the above timeout and do other things
My solution to this will be creating a class that manages Promise and Timeout instances.
Let's name this class Sleep, it takes duration in its constructor and schedule timeout upon the given duration as well as creating a promise instance.
Add an async function wait() which returns the promise instance so that we can await on.
Add a function cancel() which simply resolve the promise instance and clear timeout.
<html>
<body>
<button onClick="cancelWait()">Cancel wait</button>
<div id="text"></div>
</body>
<script lang="javascript">
class Sleep {
constructor(duration) {
this.promise = new Promise((resolve) => {
this.promiseResolve = resolve
this.timeout = setTimeout(() => {
resolve()
}, duration)
})
}
async wait() {
return await this.promise
}
cancel() {
clearTimeout(this.timeout)
this.promiseResolve()
}
}
//Usage
let sleep
const main = async () => {
const text = document.getElementById("text")
text.innerText = 'start'
sleep = new Sleep(3000)
await sleep.wait()
text.innerText = 'finish'
}
const cancelWait = () => {
sleep.cancel()
}
main()
</script>
</html>
I want to add a wait time between each forEach statement in the below code, I don't want to run into problems with rate limits so I want to wait, say 1 second, between each time the forEach runs. Is this possible?
const Discord = require("discord.js");
const client = new Discord.Client();
const config = require("./config.json");
client.on("ready", () => {
console.log(`Bot has started, with ${client.users.size} users, in ${client.channels.size} channels of ${client.guilds.size} guilds.`);
});
client.on("message", async message => {
if(message.author.bot) return;
if(message.content.indexOf(config.prefix) !== 0) return;
const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if(command === "addalltorole") {
process.setMaxListeners(n);
let role = message.guild.roles.find(r => r.name == 'TribeVerified')
if (!role) return message.channel.send(`**${message.author.username}**, role not found`)
var hasRoles = ["449032040147582977", "489252560532799488", "449032406444277760", "449032567988158465", "449032704122552320", "449032907332255759", "449033048374247426", "449033186413117441", "459119831183130645", "449033329946394645", "462285271505829909", "528059697257775106", "462061656852398090", "461635407893889037", "535632204026609665", "535632207222407168535637767242121216", "535637767242121216", "535637777388142593", "542049404270673942"];
message.guild.members.filter(member => member.roles.filter(r => hasRoles.includes(r.id)).size > 2).forEach(member => member.addRole(role));
message.channel.send(`**${message.author.username}**, role **${role.name}** was added to all applicable members`)
}
});
client.login(config.token);
I found this on stackoverflow elsewhere, not sure if it could be of use to implement what I'm trying to do?
var array = ['some', 'array', 'containing', 'words'];
var interval = 1000; // how much time should the delay between two iterations be (in milliseconds)?
array.forEach(function (el, index) {
setTimeout(function () {
console.log(el);
}, index * interval);
});
Thanks so much in advanced for any help!
Yes, that approach will work just fine.
It will space them 1 second apart regardless of how long they take to complete.
...and that's probably fine for what you are doing...
...but if you want you can use the fact that addRole returns a Promise to do some cool stuff.
For example, this uses reduce to pass along the Promises so that each call waits for the previous call to finish and then waits an additional second before making the next request:
message.guild.members.filter(member => member.roles.filter(r => hasRoles.includes(r.id)).size > 2)
.reduce(async (previous, member) => {
await previous; // wait for the previous call to finish
await new Promise(resolve => setTimeout(resolve, 1000)); // wait another second
return member.addRole(role); // pass this Promise to the next iteration
}, Promise.resolve()); // start with a resolved Promise
Here is a working demo you can run:
class Member {
constructor(name) { this.name = name; }
async addRole(role) {
await new Promise(resolve => setTimeout(resolve, 100)); // simulate asynchronous call
console.log(`added role "${role}" to ${this.name}`);
}
}
const members = [
new Member('first'),
new Member('second'),
new Member('third'),
new Member('fourth'),
new Member('fifth')
];
members.reduce(async (previous, member) => {
await previous;
await new Promise(resolve => setTimeout(resolve, 1000));
return member.addRole('admin');
}, Promise.resolve());
...but yes, either approach will work and should be just fine for what you are doing.
Im reciving websocket messages on my webpage, and i'm trying to calculate the frequency at which they are received. I do this like so:
let startTime = new Date();
ws.onmessage = function (evt)
{
prevData = recivedData;
var receivedMsg = evt.data;
recivedData = JSON.parse(receivedMsg);
const endTime = new Date();
const timeDif = endTime - startTime;
startTime = endTime;
console.log(timeDif)
}
When I take a look at the console to see what it has printed, I see that the frequency is mostly around 60 ms (as expected). Although, every fourth time or so, it will be 0 ms, I find this unlikely and I cant find what is causing it. Any ideas on how to fix this issue?
ws.onmessage = async () => {
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
await wait(50); // await between executions
// do your code
}
Update:
See this sample code below. You said that you have some loop function. Maybe it will be helpful you if you have access to the event queue every cycle and never have more than one event in the queue.
let done = false;
setTimeout(() => {
done = true
}, 5);
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
resolve();
})
);
}
const run = async () => {
while (!done) {
console.log('loop');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
What happens here. You call eventLoopQueue() function every cycle. This function pushes the callback to the queue by setImmidiate(). The callback is executed right away and anything that is in the queue are going to be executed as well. So every cycle you clear up the queue. And I believe it will help you.
I have some code that continuously updates a series of objects via network calls looks like this. I was wondering if this is bad practice and if there might be a better way. I cant use Set Interval as the time between MakeAsyncCall replies is variable and can cause a leak if the time to make the call is longer than the delay. I will be using this info to update a UI. Will this cause blocking? What are your thoughts? Let me know if you need more info.
let group = [item1, item2, item3];
// Start Loop
readForever(group, 100);
// Function to Delay X ms
const delay = ms => {
return new Promise((resolve, _) => {
const timeout = setTimeout(() => {
resolve();
}, ms);
});
};
// Function to continuously Make Calls
const readForever = async (group, ms) => {
while(true) {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
}
};
The given code won't cause any UI blocking. And is a valid way to update the UI continually.
Instead of a loop you could write it that way:
const readForever = async (group, ms) => {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
if (true) { // not needed, but there you could define an end condition
return readForever(group, ms);
}
};
In addition to the comment about the delay function:
You could directly pass the resolve to setTimeout, and because you do not cancel the Timeout anywhere you do not need to store the result setTimeout in a variable.
const delay = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};