Javascript version of a sleep function - javascript

I'm making a guessing game. When the user guesses between two foods, I want to show the calories of both foods before rendering the next component. What's Javascript's version of sleep 2?
clickHandler = e => {
this.setState({
showCalories: true
});
// PAUSE HERE FOR 2 SECONDS
if (e.target.src === this.state.mostCalories.attributes.image) {
this.setState({
currentGame: {
id: this.state.currentGame.id,
score: this.state.currentGame.score + 1,
initials: ""
}
});
this.newFoods();
} else {
this.gameOver();
}
};
I've read a few answers on here but they're either outdated or I get a parsing error. I've tried await new Promise(r => setTimeout(r, 2000)); and prefixed the function with async as stated here.

You can do this (with setTimeout)
setTimeout(() => {
//THE THINGS TO RUN AFTER X MS
}, TIME TO SLEEP IN MS)
clickHandler = e => {
this.setState({showCalories: true});
// PAUSE HERE FOR 2 SECONDS
setTimeout(() => {
if (e.target.src === this.state.mostCalories.attributes.image) {
this.setState({
currentGame: {
id: this.state.currentGame.id,
score: this.state.currentGame.score + 1,
initials: ""
}
});
this.newFoods();
} else {
this.gameOver();
}
}, 2000)
}

Make the event handler an async function. And simply await a timeout Promise.
clickHandler = async e => {
this.setState({
showCalories: true
});
// PAUSE HERE FOR 2 SECONDS
await new Promise(r => setTimeout(r, 2000))
if (e.target.src === this.state.mostCalories.attributes.image) {
this.setState({
currentGame: {
id: this.state.currentGame.id,
score: this.state.currentGame.score + 1,
initials: ""
}
});
this.newFoods();
} else {
this.gameOver();
}
};

You can just use a timeout
clickHandler = e => {
this.setState({
showCalories: true
});
window.setTimeout(() => {
if (e.target.src === this.state.mostCalories.attributes.image) {
this.setState({
currentGame: {
id: this.state.currentGame.id,
score: this.state.currentGame.score + 1,
initials: ""
}
});
this.newFoods();
} else {
this.gameOver();
}
}, 2000);
};

You can do it inside an asynchronous function. But unlike in case of JAVA's sleep, you can't use it in synchronous operation.
async function sleep(seconds) {
console.log('start');
await new Promise( resolve => {
console.log('now on sleep...');
setTimeout(resolve, seconds);
});
console.log('end');
}
sleep(3000);

Related

How to combine p-cancelable and p-progress npm packages to get a cancelable progress reporting promise

I would like to have a cancelable promise that reports progress at the same time. Something like a combination of p-progress and p-cancelable. And while usage of any of them separately is simple I'm struggling a bit to combine them both.
This is what I tried so far which successfully reports progress but throws cancelablePromise.cancel is not a function error.
import PCancelable from 'p-cancelable';
import PProgress from 'p-progress';
const cancelablePromise = doJobWithProgress();
try {
cancelablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
cancelablePromise.cancel();
const result = await cancelablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return PProgress((progressCallback) => {
return new PCancelable((resolve, reject, onCancel) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
}
catch (error) {
console.log('Promise catch error: ' + error);
reject(error);
}
});
});
}
To make this complete - I ended up, as #Bergi suggested, with a different approach i.e., using AbortController and extending the EventEmitter class (I'm coding in NodeJS - for the web you can inherit EventTarget).
Maybe someone will be interested in answer to the original question - when trying to combine the two libraries I ended up creating a new CancelableProgressPromise class and copy-pasting the most necessary parts from the p-progress library to it and extending the PCancelable:
import PCancelable from "p-cancelable";
export default class CancelableProgressPromise extends PCancelable {
constructor(executor) {
const setProgress = progress => {
if (progress > 1 || progress < 0) {
throw new TypeError('The progress percentage should be a number between 0 and 1');
}
(async () => {
// We wait for the next microtask tick so `super` is called before we use `this`
await Promise.resolve();
// Note: we don't really have guarantees over
// the order in which async operations are evaluated,
// so if we get an out-of-order progress, we'll just discard it.
if (progress <= this._progress) {
return;
}
this._progress = progress;
for (const listener of this._listeners) {
listener(progress);
}
})();
};
super((resolve, reject, onCancel) => {
executor(
value => {
setProgress(1);
resolve(value);
},
reject,
onCancel,
progress => {
if (progress !== 1) {
setProgress(progress);
}
},
);
});
this._listeners = new Set();
this._setProgress = setProgress;
this._progress = 0;
}
get progress() {
return this._progress;
}
onProgress(callback) {
if (typeof callback !== 'function') {
throw new TypeError(`Expected a \`Function\`, got \`${typeof callback}\``);
}
this._listeners.add(callback);
return this;
}
}
Usage example:
import CancelableProgressPromise from './cancelableProgressPromise.js';
const progressablePromise = doJobWithProgress();
try {
progressablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
progressablePromise.cancel();
const result = await progressablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return new CancelableProgressPromise((resolve, reject, onCancel, progressCallback) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
} catch (error) {
reject(error);
}
});
}
// Output:
// progress: 0.1
// progress: 0.2
// progress: 0.30000000000000004
// progress: 0.4
// canceled
// Main catch error: CancelError: Promise was canceled

Use jest for testing timeouts calling recursive function

I want to test the following code:
const poll = (maxTries, interval, channel, stopTime) => {
let reached = 1;
const someInformation = someGetter();
const fetchData = async (resolve, reject) => {
const data = await networkClass.someApiCall();
if (data.stopTime === 1581516005) {
console.log("cond true");
someInformation.meta1 = transFormer(someInformation);
someInformation.meta2 = transFormer(someInformation);
someInformation.meta3 = {
...someInformation.meta1,
data,
};
resolve(someInformation);
} else if (reached < maxTries) {
reached += 1;
console.log("do it again");
setTimeout(fetchData, interval, resolve, reject);
} else {
reject(new Error('max retries reached'));
}
};
return new Promise(fetchData);
};
const checkForUpdates = () => {
setTimeout(() => {
poll(/* max retries */ 10, /* polling interval */ 1000, channel, stopTime)
.then((res) => {
setData(res);
console.log({ res: res.meta3.data });
})
.catch((e) => console.log({ e }));
}, 20000);
};
The test looks like that:
it(`should do stuff`, () => {
jest.spyOn(networkClass, 'someApiCall')
.mockResolvedValueOnce({ stopTime })
.mockResolvedValueOnce({ stopTime })
.mockResolvedValueOnce({ stopTime: 1581516005 });
checkForUpdates();
jest.advanceTimersByTime(40000);
expect(setDataMock).toHaveBeenCalled();
});
That console.log (console.log("do it again");) is only printed once, as if the test would not be able to call a setTimeout within a setTimeout. Do you have any ideas what might help?

How to schedule multiple timeout functions

I have a scenario where I need to schedule multiple timeouts in vanilla JS, it will be something like this below:
const scheduler = {
done: function() {},
schedule: function() {}
};
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(1);
done()
}, 2000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(2);
done()
}, 1000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(3);
done()
}, 3000);
});
it should execute it in such a way that it must print below:
1
2
3
This is a nice use case of promises.
These objects allow the abstraction over asynchronous operations, and fortunately, can be chained.
const scheduler = {
last: Promise.resolve(),
schedule(cb){
this.last = this.last.then(() => new Promise(cb))
}
}
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(1);
done()
}, 2000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(2);
done()
}, 1000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(3);
done()
}, 3000);
});
However, if you use this with promises you obtained from elsewhere , it can quickly turn into the Explicit Promise construction antipattern, so, for a more general code, move the new Promise() part out of the schedule method:
const scheduler = {
last: Promise.resolve(),
schedule(cb){
this.last = this.last.then(cb)
}
}
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(1);
done()
}, 2000);
})
});
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(2);
done()
}, 1000);
})
});
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(3);
done()
}, 3000);
})
});
You would need a que for the timeouts, eg:
const scheduler = {
done:function(){}, // Not sure what this function is supposed to do
schedule:function(callback, timeout) {
scheduler.que.push([callback, timeout]); // Add the function along with its wait time
if(scheduler.que.length == 1) scheduler.startQue(); // If the que was previously empty, start the execution of the que
},
startQue() {
setTimeout(() => {
scheduler.que[0][0](); // run the callback timeout
scheduler.que.shift(); // Remove the just ran timeout
if(scheduler.que.length >= 1) scheduler.startQue(); // Start another timeout if we still have some in the que
}, scheduler.que[0][1]);
},
que: []
};
scheduler.schedule(() => console.log(1), 2000); // Wait for 2 seconds to print
scheduler.schedule(() => console.log(2), 1000); // Wait 1 second after the first one printed
scheduler.schedule(() => console.log(3), 3000); // Wait another 3 seconds after the second one
const scheduler = {
done: function (cb) {
cb();
this.timeouts.shift();
this.timeouts[0] && this.timeouts[0]();
},
schedule: function (cb, ms) {
const timeout = () => setTimeout(this.done.bind(this), ms, cb);
this.timeouts.push(timeout) == 1 && timeout();
},
timeouts: [],
};
scheduler.schedule(() => console.log('1'), 2000);
scheduler.schedule(() => console.log('2'), 1000);
scheduler.schedule(() => console.log('3'), 3000);

How to repeatedly call asynchronous function until specified timeout?

I want to keep calling asnchronous api requests repeatedly until it exceeds specified time. Using async-retry we can only specify retrycount and interval, we wanted to specify even timeout in the parameter.
Can you just suggest a way?
// try calling apiMethod 3 times, waiting 200 ms between each retry
async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
// do something with the result
});
Here is what you want :
const scheduleTrigger = (futureDate) => {
const timeMS = new Date(futureDate) - new Date();
return new Promise((res, ref) => {
if (timeMS > 0) {
setTimeout(() => {
res();
}, timeMS);
} else {
rej();
}
})
}
//const futureDate = '2020-07-23T20:53:12';
// or
const futureDate = new Date();
futureDate.setSeconds(futureDate.getSeconds() + 5);
console.log('now');
scheduleTrigger(futureDate).then(_ => {
console.log('future date reached');
// start whatever you want
stopFlag = false;
}).catch(_ => {
// the date provided was in the past
});
const wait = (ms = 2000) => {
return new Promise(res => {
setTimeout(_ => {
res();
}, ms);
})
}
const asyncFn = _ => Promise.resolve('foo').then(x => console.log(x));
let stopFlag = true;
(async () => {
while (stopFlag) {
await asyncFn();
await wait();
}
})();
So you want to keep retrying for as long as its within a certain timeout? How about this:
// Allow retry until the timer runs out
let retry = true;
const timeout = setTimeout(() => {
// Set retry to false to disabled retrying
retry = false;
// Can also build in a cancel here
}, 10000); // 10 second timeout
const retryingCall = () => {
apiMethod()
.then(response => {
// Optionally clear the timeout
clearTimeout(timeout);
})
.catch(() => {
// If retry is still true, retry this function again
if (retry) {
retryingCall();
}
});
};
You can achieve what you want with this function:
const retryWithTimeout = ({ timeout, ...retryOptions}, apiMethod, callback) => {
let timedout = false;
const handle = setTimeout(
() => (timedout = true, callback(new Error('timeout'))),
timeout
);
return async.retry(
retryOptions,
innerCallback => timedout || apiMethod(innerCallback),
(err, result) => timedout || (clearTimeout(handle), callback(err, result))
)
};
It has the advantage of allowing you to use the functionality of async.retry, as you apparently want, and also allows the timeout to take place even when what exceeds the timeout is the apiMethod itself, not the waiting time.
Usage example:
retryWithTimeout(
{timeout: 305, times: 4, interval: 100},
(callback) => { callback('some api error'); },
(err, result) => console.log('result', err, result)
)

How to make this code sequential without touching outer function

I need such sequence:
🤡lurks in the shadows;
the end
How to achieve such result without touching 'sequential' function?
Achieve sequential run
function who() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 200);
});
}
function what() {
return new Promise(resolve => {
setTimeout(() => {
resolve('lurks');
}, 300);
});
}
function where() {
return new Promise(resolve => {
setTimeout(() => {
resolve('in the shadows');
}, 500);
});
}
async function msg() {
const a = await who();
const b = await what();
const c = await where();
console.log(`${ a } ${ b } ${ c }`);
}
function sequential(){
msg();
console.log('the end');
}
sequential();
I need such sequence:
🤡lurks in the shadows
the end
How to achieve such result without touching 'sequential' function?
Current result is:
the end
🤡lurks in the shadows
function who(data) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data + '🤡');
}, 200);
});
}
function what(data) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data + 'lurks');
}, 300);
});
}
function where(data) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data + 'in the shadows');
}, 500);
});
}
function msg() {
return who('')
.then(what)
.then(where);
}
function sequential(){
return msg()
.then((respo) => {
console.log(JSON.stringify(respo));
console.log('the end');
});
}
sequential();
You can do this!

Categories

Resources