Quite new to coding in general and not used async/await much before.
I'm trying to get an enemy in a little maze game I'm making to move up, left, down, right, repeat.
Problem I have is that the function just executes everything at once instead of waiting. I know I'm missing something, but can't put my finger on it. Can anyone advise?
async function enemyMovement() {
enemyCSSVertical = -50;
enemyCSSHorizontal = 400;
enemyX = 9;
enemyY = 10;
await enemyUp();
await enemyLeft();
await enemyDown();
await enemyRight();
}
// move up
async function enemyUp() {
setTimeout(() => {
console.log("up");
enemyCSSVertical += -50;
enemy.css("top", enemyCSSVertical);
}, 1000);
}
//move left
async function enemyLeft() {
setTimeout(() => {
console.log("left");
enemyCSSHorizontal += -50;
enemy.css("left", enemyCSSHorizontal);
}, 1000);
}
async function enemyDown() {
setTimeout(() => {
console.log("down");
enemyCSSVertical += 50;
enemy.css("top", enemyCSSVertical);
}, 1000);
}
async function enemyRight() {
setTimeout(() => {
console.log("right");
enemyCSSHorizontal += 50;
enemy.css("left", enemyCSSHorizontal);
}, 1000);
}
You need to create a promise in your move functions and resolve it within the timeout
async function enemyUp() {
var p = new Promise(function(resolve,reject) {
setTimeout(() => {
console.log("up");
enemyCSSVertical += -50;
enemy.css("top", enemyCSSVertical);
resolve("Finished");
}, 1000);
}
return p;
}
Related
I have an app that animates a value. Below, if to is set, the amount lerps to it.
const lerp = (v0, v1, t) => {
return (1 - t) * v0 + t * v1;
}
const app = {
to: false,
amount: 20,
animate(){
requestAnimationFrame(this.animate.bind(this));
if(this.to !== false){
this.amount = lerp(this.amount, this.to, 0.1)
if(Math.abs(this.amount - this.to) < 0.001){
this.amount = this.to;
this.to = false;
}
console.log(this.amount);
}
},
init(){
this.animate();
}
}
app.init();
console.log("Waiting to start");
setTimeout(() => {
console.log("Started!");
app.to = 0;
}, 1000)
This works great. But I'd like to call a function when it finishes the process, and that function may change. Ideally, I'd like to add it like so:
...
promise: null,
animate(){
requestAnimationFrame(this.animate.bind(this));
if(this.to !== false){
this.amount = lerp(this.amount, this.to, 0.1)
if(Math.abs(this.amount - this.to) < 0.001){
this.amount = this.to;
this.to = false;
// Resolve the promise so logic can continue elsewhere
if(this.promise) this.promise.resolve();
}
}
console.log(this.amount);
},
stop(){
this.promise = something... // Not sure what this should be
await this.promise;
// subsequent logic
nextFunction()
}
I can't get my head around how I can properly set this up. Any help welcome.
Wrapper entire function in promise and then resolve it
const lerp = (v0, v1, t) => {
return (1 - t) * v0 + t * v1;
}
const app = {
to: false,
amount: 20,
animate() {
return new Promise(resolve => {
const inner = () => {
requestAnimationFrame(inner.bind(this));
if (this.to !== false) {
this.amount = lerp(this.amount, this.to, 0.1)
if (Math.abs(this.amount - this.to) < 0.001) {
this.amount = this.to;
this.to = false;
resolve()
}
}
console.log(this.amount);
}
inner()
})
},
init() {
return this.animate();
}
}
app.init().then(() => alert('over'));
setTimeout(() => {
app.to = 0;
}, 1000)
In order to create a promise for an arbitrary action (rather than a promise for the whole animate loop like Konrad showed) I used something from this answer: https://stackoverflow.com/a/53373813/630203 to create a promise I could resolve from anywhere.
const createPromise = () => {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
const lerp = (v0, v1, t) => {
return (1 - t) * v0 + t * v1;
}
const app = {
to: false,
amount: 20,
animateToPromise: null,
animateToResolve: null,
animate(){
requestAnimationFrame(this.animate.bind(this));
if(this.to !== false){
this.amount = lerp(this.amount, this.to, 0.1)
if(Math.abs(this.amount - this.to) < 0.001){
this.amount = this.to;
this.to = false;
if(this.animateToPromise){
this.animateToResolve();
this.animateToPromise = null;
this.animateToResolve = null;
}
}
console.log(this.amount);
}
},
init(){
this.animate();
},
async animateTo(n){
const { promise: animateToPromise, resolve: animateToResolve } =
createPromise();
this.to = n;
this.animateToPromise = animateToPromise;
this.animateToResolve = animateToResolve;
await this.animateToPromise;
return true;
}
}
app.init();
console.log("Waiting to start");
(async () => {
setTimeout(async () => {
console.log("Started!");
await app.animateTo(0);
console.log("Got to 0!");
console.log("now for 20");
await app.animateTo(20);
console.log("done :)");
}, 1000)
})();
This means I can queue my promises with a single animate function running.
I have a function that I need to run multiple times but wait until the previous one has finished.
Currently What I have is:
for(var x = 0; x < rollsArr.length; x++){
rollDice(data.results, x)
}
function rollSkins(results, rollN) {
// do some stuff
setTimeout(function() {
roll(results, rollN)
}, 500);
}
function roll(results, rollN) {
// do some stuff
setTimeout(function() {
// do some stuff
}, 8500);
}
I would like a roll function to finish then wait a few seconds and then start another roll. I tried awaiting rollDice but I didn't implement it correctly or it did not work.
What would be the solution for this?
You can use async/await or Promise.then(). Reference: https://zellwk.com/blog/async-await-in-loops/
const rollsArr = [1,2,3];
const data = {results: "example"};
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms))
}
(async () => {
for (var x = 0; x < rollsArr.length; x++){
await rollDice(data.results, x)
}
})()
async function rollDice(results, rollN) {
// do some stuff
await sleep(500);
await roll(results, rollN);
}
async function roll(results, rollN) {
// do some stuff
console.log("Rolling dice");
await sleep(1000);
console.log("Doing stuff");
}
There's something about chaining promises that I don't understand. The Node.js snippet below produces the following output. Why is promise.allSettled called after the first sleep on line 18 and not after the 2nd one on line 21?
Cycle 0 is going to sleep promise.js:2
Cycle 0 slept for 2 seconds promise.js:6
Returned from sleep function promise.js:19
Cycle 0 is going to sleep promise.js:2
Done with the process promise.js:27
Cycle 0 slept for 2 seconds promise.js:6
function sleep(cycle) {
console.log(`Cycle ${ cycle } is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${ cycle } slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 1;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i).then(() => {
console.log('Returned from sleep function');
sleep(i);
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
Because you haven't resolved the promise created sleep(i).then to the promise from the second sleep, so it doesn't wait for that second operation to finish before settling. You need a return in front of the second call:
function sleep(cycle) {
console.log(`Cycle ${ cycle } is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${ cycle } slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 1;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i).then(() => {
console.log('Returned from sleep function');
return sleep(i); // <============================
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
Here's a version with two cycles, and with the sleep calls more clearly marked:
function sleep(cycle, sub) {
console.log(`Cycle ${cycle}(${sub}) is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${cycle}(${sub}) slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i, "a").then(() => {
console.log(`Returned from sleep function (${i})`);
return sleep(i, "b"); // <============================
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
It might be clearer to split that sleep, sleep serial operation off into its own function, perhaps an async function:
async function twoSleep(i) {
await sleep(i, "a");
console.log(`Returned from sleep function (${i})`);
await sleep(i, "b");
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(twoSleep(i));
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
function sleep(cycle, sub) {
console.log(`Cycle ${cycle}(${sub}) is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${cycle}(${sub}) slept for 2 seconds`);
resolve();
}, 2000);
});
}
async function twoSleep(i) {
await sleep(i, "a");
console.log(`Returned from sleep function (${i})`);
await sleep(i, "b");
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(twoSleep(i));
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
The issue is that Promise.allSettled runs all promises in parallel, so all of them resolve at the same time. If you want to run them one by one try
async function process() {
const cycles = 1;
for (let i = 0; i < cycles; i++) {
await sleep(i).then(() => {
console.log("Returned from sleep function");
return sleep(i);
});
}
console.log("Done with the process");
}
process();
In a browser extension I'm trying to:
find a button
update its text every second for 10 seconds
invoke submit
Simple enough, but unfortunately I'm a JavaScript novice.
I'm clueless: Why does the code below not reach line #15 (after await)?
const Timeout = 10000;
const CountdownStep = 1000;
async function scheduleSubmit(node, timeout) {
originalTextContent = node.textContent;
while (timeout > 0) {
console.log(`Timeout: ${timeout}`);
try {
await new Promise((resolve => setTimeout(() => {
console.log(`[Promise] Timeout: ${timeout}`);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= CountdownStep;
console.log(`[Promise] Timeout: ${timeout}`);
}, CountdownStep)));
console.log('Hello? Helloooooooo??');
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), Timeout);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
If you extract a simple delay helper that returns a Promise that resolves after the specified amount of time you code becomes way more readable. Not to speak you can test and debug your code separately to identify the issue.
const SUBMIT_TIMEOUT = 10000;
const COUNT_DOWN_STEP = 1000;
function delay(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout))
}
async function scheduleSubmit(node, timeout) {
const originalTextContent = node.textContent;
while (timeout > 0) {
try {
await delay(COUNT_DOWN_STEP);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= COUNT_DOWN_STEP;
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), SUBMIT_TIMEOUT);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
You just need to resolve the Promise calling resolve():
const Timeout = 10000;
const CountdownStep = 1000;
async function scheduleSubmit(node, timeout) {
originalTextContent = node.textContent;
while (timeout > 0) {
console.log(`Timeout: ${timeout}`);
try {
await new Promise((resolve => setTimeout(() => {
console.log(`[Promise] Timeout: ${timeout}`);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= CountdownStep;
console.log(`[Promise] Timeout: ${timeout}`);
resolve();
}, CountdownStep)));
console.log('Hello? Helloooooooo??');
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), Timeout);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
Docs: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise
I would like to setup a counter which can be paused as well as resumed with in a button click in javascript. But what ever i have tried can able to pause it but its not resuming from where i have paused, its starting from beginning when i perform resume operation.
below is my code what i have tried so far.
const [isPlay, setisPlay] = useState(true);
const timerRef = useRef(null);
useEffect(() => {
start();
}, []);
var counter;
function start() {
console.log('start');
if (!counter) {
reset();
} else {
loop();
}
}
function pause() {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
}
function reset() {
console.log('reset');
pause();
counter = 10;
loop();
}
function loop() {
timerRef.current = setInterval(function () {
if (0 >= counter) {
pause();
return;
}
console.warn('counter', counter);
counter--;
}, 1000);
}
const stopProgress = () => {
if (isPlay) {
pause();
setisPlay(false);
} else {
start();
setisPlay(true);
}
};
Can anyone please give me a solution for this to fix that. Answers would be much appreciated.