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);
Related
var functionsArray = [
function() {
setTimeout(function() {
console.log(1);
}, 100);
},
function() {
setTimeout(function() {
console.log(2);
}, 200);
},
function() {
setTimeout(function() {
console.log(3);
}, 10);
}
],
Say I have an array of functions like above(the number of functions in it is not known). I want to write a function which takes this array as parameter and executes them in sequence. In the example above, I want it to log 1,2,3 in the sequence. Since Promise.all does not guarantee the order of execution, is it possible to achieve this without callback hell?
You can't get a promise from a function that just calls setTimeout - it needs some help, e.g.:
function after(n, f) {
return () => new Promise(resolve => {
setTimeout(() => {
resolve(f());
}, n);
});
}
with usage:
var functionsArray = [
after(100, () => console.log(1)),
after(200, () => console.log(2)),
after( 10, () => console.log(3)),
];
With that array you can then just await each function in turn:
for (let f of functionsArray) {
await f();
}
You can write an simple setTimeoutPromise function:
function timeoutPromise(time = 0){
return new Promise(resolve => setTimeout(resolve, time)
}
await timeoutPromise(10);
console.log('waited 10 sec')
timeoutPromise(20).then(() => console.log('waited 20 sec')
//make a new array as you like
Promise.all([
timeoutPromise(100).then(() => console.log(1)),
timeoutPromise(200).then(() => console.log(2)),
timeoutPromise(300).then(() => console.log(3))
])
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)
)
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);
This is just such a task, challenge, literally, everything that I have outlined is all information
We can add another argument for callback 'all is done'.
Important, console.log('all is done') need to be displayed only after all
'timer' functions have completed their execution, regardless of their call order.
I can’t figure out which way to approach the task, I need to output using callback, but I need to wait for all calls to be completed
What we have:
//Write a “timer” function which takes a function and timeout, after all executions fire a callback (‘all is done’):
const timer = // your code
timer( () => {
console.log(500)
}, 500 )
timer( () => {
console.log(3000)
}, 3000 )
timer( () => {
console.log(1500)
}, 1500 )
timer( () => {
console.log(2500)
}, 2500 )
If it is a promise related question, your constance returns a function with promise:
const timer = (timeout) => {
return new Promise(function(resolve, reject) {
setTimeout(function(timeout) {
resolve(() => { console.log(timeout)});
}, timeout);
});
}
Promise.all().then( () => { alert("All is done") })
This is how I solved the problem.
It would be cool to solve it without using a global variable.
//Write a “timer” function which takes a function and timeout, after all executions fire a callback (‘all is done’):
let maxTimer = 0;
const timer = async (toRun, timeout) => {
++maxTimer
const done = await new Promise((resolve, reject) => {
setTimeout(() => {
toRun()
resolve()
}, timeout)
} )
if (maxTimer === 0){
console.log('all is done')
}
}// your code
timer( () => {
console.log(6000)
--maxTimer
}, 6000 )
timer( () => {
console.log(500)
--maxTimer
}, 500 )
timer( () => {
console.log(1500)
--maxTimer
}, 1500 )
timer( () => {
console.log(7000)
--maxTimer
}, 7000 )
timer( () => {
console.log(2500)
--maxTimer
}, 2500 )
I need to finish some query no sooner than 500ms.
I don't want to waste that 500ms either, like delaying everything like here:
someFunction(someService) {
setTimeout(() => {
getDataFromBackend().then(data => {
someService.makeAnAction();
});
}, 500);
}
How to start getting the data from the backend at once but do something with that data not too soon?
Make a function to get a promise that resolves in the requisite time:
const delay = ms => new Promise(resolve => {
setTimeout(resolve, ms);
});
Wait for both it and your operation to complete:
someFunction(someService) {
return Promise.all([getDataFromBackend(), delay(500)]).then(([data]) => {
someService.makeAnAction();
});
}
Alternative without destructuring, with Bluebird:
someFunction(someService) {
const dataGet = getDataFromBackend();
return delay(500).return(dataGet).then(data => {
someService.makeAnAction();
});
}
Potentially misleading equivalent of the above:
someFunction(someService) {
return delay(500)
.return(getDataFromBackend())
.then(data => {
someService.makeAnAction();
});
}
Async/await:
async someFunction(someService) {
const dataGet = getDataFromBackend();
await delay(500);
const data = await dataGet;
someService.makeAnAction();
}