JS - Why does await not work in class - javascript

I need to wait some time inside a function of a class. I tried to adapt Jonas W. answer: https://jsfiddle.net/5wk2cohe/
const sleep = ms => new Promise(res => setTimeout(res, ms));
class K {
run(n) {
(async function() {
document.body.textContent = n;
await sleep(1000);
})();
}
}
var v = new K();
for (let n = 0; n < 4; n++) {
v.run(n);
}
But the counts prompts immediately 3. If I run https://jsfiddle.net/tctxcn9o/
I see a counter as excepted:
const sleep = ms => new Promise(res => setTimeout(res, ms));
(async function() {
for(let n = 0; n < 4; n++) {
document.body.textContent = n;
await sleep(1000);
}
})();
What I am doing wrong?
[Update] Some background: In my hometown a very old programm called JavaKara is very popular to help students at the high school to lern how to code:
To see how the ladybug moves is very helpful for understanding and finding your error. So I am trying to wait some time after each step of the ladybug.

As El Aoutar Hamza said, you're creating 4 async tasks where every task is run instantly. In order to delay the next task, you need to use await inside the loop and make the run function return a Promise, something like this:
const sleep = ms => new Promise(res => setTimeout(res, ms));
class K {
async run(n) {
document.body.textContent = n;
await sleep(1000);
}
}
(async function() {
var v = new K();
for (let n = 0; n < 4; n++) {
await v.run(n);
}
})();

In your first example, you are creating 4 async functions that are not related to each other, so none of them has to wait for the other.
While in your second example, you are creating 1 async function, where the await expression causes the async function execution to pause until the promise sleep is fulfilled.

Related

Make JS function output while function running

Javascript code
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 1000000;
var money = 0
var i = 0
let dollars = document.getElementById("total")
let dollarclick = document.getElementById("click1")
let autoclick = document.getElementById("buyauto")
dollarclick.addEventListener("click", clicked)
autoclick.addEventListener("click", auto)
function clicked() {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
}
function auto() {
autoclick.disabled = true
console.log("function skull")
while (i<10) {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
sleep(1000)
console.log(money)
i++
}
}
How to make the function output while it is running, and not let it output after the function is already finished? Keep in mind I am making a cookie clicker type game, with the auto being able to be purchased only once. Can you help me resolve this problem? The game should be able to give the points every second, and not after the function has finished. Please help me, thank you!
You can do this in an asynchronous manner.
I have used a function (setTimeoutAsync from #3 here) that resolves a promise after a timeout, and an asynchronous recursive function (nClicks(n)) that uses await for the timeout before performing the action and awaits recursion n times before returning.
Then, the asynchronous event handler just needs to disable the button and wait for nClicks(10) to complete before doing other stuff like re-enabling the button. If you don't need to do other stuff afterwards you can omit the async declaration on the handler and just call nClicks(10) without await.
const setTimeoutAsync = function(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, timeout)
})
}
let money = 0
const dollars = document.getElementById('total')
const tick = () => {
dollars.innerHTML = `$${++money}`
console.log('tick', dollars.innerHTML)
}
const nClicks = async(n) => {
if (n) {
// wait one second
await setTimeoutAsync(1000)
// then incriment
tick()
// wait for recursion to complete
await nClicks(--n)
}
}
const dollarclick = document.getElementById('click1')
const autoclick = document.getElementById('buyauto')
dollarclick.addEventListener('click', tick)
autoclick.addEventListener('click', async() => {
autoclick.disabled = true
// wait for 10 cycles
await nClicks(10)
autoclick.disabled = false
})
const autoclickOnce = document.getElementById('buyautoOnce')
autoclickOnce.addEventListener('click', () => {
autoclickOnce.disabled = true
nClicks(10)
})
<h1 id="total">$0</h1>
<button id="click1">+1</button>
<button id="buyauto">Auto</button>
<button id="buyautoOnce">Auto Once</button>

Javascript : Call function and wait seconds then

I feel stupid because I do not find what I want to do...
It is in PURE Javascript.
I wan't to call a function, and stop it (or kill it, or whatever) next some seconds.
Here is my actual code :
function scrollThumbnails() {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSlider(thumbnail);
resetSliderPosition(thumbnail);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
async function verticalSlider(element) {
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
while (toTop > -max) {
await new Promise((resolve) => setTimeout(resolve, 50));
toTop--;
slider.style.top = toTop + 'px';
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
function resetSliderPosition(element) {
const slider = element.querySelector('.vertical-carrousel');
slider.style.removeProperty('top');
}
So here, i want to call 'verticalSlider' in my loop, but if it is over than 45 seconds, I want to stop it and go on the next thumbnail.
Is it possible ? Do I miss something ?
Thanks by advanced :)
This is simple example without async and await with manual timeout counter implementation.
// Function, which accepts time out value in seconds (counting from 0)
const myFunct = (timeOutSec) => {
// Get current time
const ct = Date.now();
// Set counter for example function logic
let secCounter = ct - 1000;
// Start eternal loop
while(true) {
// Get in loop time
const ilt = Date.now();
// Compare current time and in loop time
// If current time + timeout sec < in loop time, break it
if(ct + timeOutSec * 1000 < ilt) break;
// Here do main function logic in loop
// for example, console log each second
if(ilt - secCounter > 1000) {
console.log(`time ${ilt}`);
secCounter += 1000;
}
}
}
// Test running function for 4 sec
myFunct(3);
console.log(`Completed at ${Date.now()}`);
Or similar with promises
// Async timeout function
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Promise function which accepts timout value in seconds (counting from 0)
const myPFunct = (timeOutSec) => {
return new Promise(async (resolve, reject) => {
// Current time
const ct = Date.now();
// start eternal loop
while(true) {
// In loop time
const ilt = Date.now();
// Check in-loop time, current time and timeout
if(ct + timeOutSec * 1000 < ilt) {
resolve(`completed at ${ilt}`);
break;
}
// Do something
console.log(`time: ${ilt}`);
// Wait 1 sec
await timeout(1000);
}
});
}
// Test async function
const test = async () => {
// Run myPFunct for 4 sec
const result = await myPFunct(3);
console.log(`Test function result: ${result}`);
}
// Run test function
test();
Thanks to #tarkh solution.
If someone try to do an horizontal slider like me, or something with loop, I have to modify the code (because with the source solution, the resolve goes up to all the async methods).
Here is the final solution (I have to name the loop to break it and resolve the current promise - there are maybe simplest solution ?) :
window.onload = function () {
scrollThumbnails();
}
const scrollThumbnails = async () => {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSliderWithTimeout(thumbnail, 45000);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const verticalSliderWithTimeout = (element, timeoutDelay) => {
return new Promise(async (resolve, reject) => {
const currentTime = Date.now();
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
slider.style.top = toTop + 'px';
sliderLoop: while (toTop > -max) {
const inLoopTime = Date.now();
if(currentTime + timeoutDelay < inLoopTime) {
break sliderLoop;
}
await timeout(50);
toTop--;
slider.style.top = toTop + 'px';
}
resolve();
});
}
HTML code if needed (Handlebars) :
<div class="thumbnail">
<div class="photos-slider">
<div id="vertical-carrousel" class="vertical-carrousel">
{{#each this.photos}}
<img src="/image/{{../this.agency.product_type}}/{{../this.agency.id}}/{{../this.reference}}/{{urlencode this}}"
onerror="{{this}}" />
{{/each}}
</div>
</div>
</div>

JavaScript: async function that would retry for a period of time at a given interval

I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}

how to delay execution inside of return new promise

I'm working on a web-based visualization tool of algorithms using javascript, so it needs some pause during the execution. To pause execution I used const delay = ms => new Promise(res => setTimeout(res, ms)); and called function await delay(500);, which I found on stack overflow, it worked fine with a non-recursive algorithm. And to make recursive algorithm sequential I used return new Promise((resolve,reject) and .then(). but now I can't use await inside of return new Promise, what is the reason, and am I on right track?
my code looks like this(quicksort)
const quick = async(low, high) =>
{
return new Promise((resolve,reject) =>
{
if (low < high)
{
var pivot = color[high]
var i = low -1
for(var j = low; j <= high - 1; j++)
{
// I want some delay here
if(color[j] < pivot)
{
i++
swap(i,j)
// delay here
}
}
var pi = i+1;
swap(pi,high)
// and delay here
quick( low, pi - 1).then(quick( pi + 1, high));
}
resolve();
});
}
You don't need return new Promise(... because async functions already return promises. You can't use await because the promise handler is not an async function. Try this:
const quick = async (low, high) => {
if (low < high) {
var pivot = color[high];
var i = low - 1;
for (var j = low; j <= high - 1; j++) {
await delay(500);
if (color[j] < pivot) {
i++;
swap(i, j);
// delay here
}
}
var pi = i + 1;
swap(pi, high);
await delay(500);
await quick(low, pi - 1);
await quick(pi + 1, high);
}
// return is implicit
};

Repeat sending the request to another server until the expected result is achieved

I'm requesting to server "S" to get some data, but this data may not be ready.
When the data is not yet ready, server S responds with {"data":null,"state": "pending"} but when the data has been prepared the response will be something like {"data": {...somedata}, "state": "done"}.
I have to repeat the request until the data is ready. What I'm doing now is something like this:
let wait = function* () {
let t = 500;
for (let j = 1; j < 10; j++) {
yield new Promise((resolve) => {
setTimeout(() => resolve(), t*=2);
});
}
}();
let result = await sendRequestToS();
status = result;
for (let i = 0; i < 4 && result.state==='pending'; i++) {
await wait.next().value;
result = await sendRequestToS();
}
As you can see, I send the request up to 4 times with a delay of 1, 2, 4 and 8 seconds.
Am I doing this the right way?
Isn't that (using setTimeout to delay between requests) a bad practice?
I'd write this as such:
function wait(ms) {
return new Promise(res => setTimeout(res, ms));
}
async function requestAndRetry() {
let retries = 10;
let timeout = 1000;
while(retries>0) {
const response = await sendRequestToS();
if (result?.state === 'done') {
return result;
}
await wait(timeout);
retries--;
timeout*=2;
}
throw new Error('Request failed after 10 retries');
}
I don't think it's a bad idea. It's called exponential back-off and you're not blocking the script engine.
Instead of using generators directly, you could simply do this using async/await and recursion. Here's an example which tries to get the response a limited number of times in order to prevent an endless recursion and with a timeout between retries:
async function wait(timeInMs) {
console.log('Waiting ...');
return new Promise((resolve => setTimeout(() => resolve(), timeInMs)));
}
async function tryRequestToS(numberOfTries, timeout) {
if (numberOfTries <= 0) {
throw new Error("could net get result");
}
const result = await sendRequestToS();
if (result && result.state === "done") {
return result;
}
await wait(timeout); // wait for the defined timeout before recurring
return tryRequestToS(numberOfTries - 1, timeout);
}
(async () => {
try {
const result = await tryRequestToS(10, 500); // max. 10 retries, 500 ms delay between retries
console.log(result);
} catch(err) {
console.log(err);
}
})();

Categories

Resources