Periodically resolving a promise in javascript - javascript

Let’s say I want to resolve some promise every 2 seconds AFTER the function is run.
I tried a pattern like this at first: setInterval(MyAsyncFunction, 2000), but of course this does not wait for the promise to resolve before calling the function again.
I also tried setInterval(async () => await MyAsyncFunction(), 2000), but this is basically the same thing since I’m just wrapping it in another promise that will resolve while the setInterval is ticking down.
Is there a way to do what I am trying to do with setInterval or is there some alternative way to do this using setTimeout?

A Promise resolves once only, so therefore cannot, by definition, resolve periodically.
If you define a delay function, which returns a promise that resolves after timeout ms...
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
you could use it in an async loop:
async function foo(){
for(let i = 0; i < 100; ++i){
console.log("azrael");
await delay(2000);
}
}

If what you want is to call async function, then await for it, then wait extra fixed time, then repeat, you may approach it following ways.
You can call setTimeout from that function itself, so it will schedule its next run when it's finished:
async function myAsyncFunction() {
return new Promise(resolve => setTimeout(() => {
resolve('result'); // resolves, then
setTimeout(async () => { // schedules
let result = await myAsyncFunction(); // self-call
console.log('result') // and handles the result
}, 2000); // after extra 2s
}, 2000)); // imitates async function with 2s delay
}
async function main() {
let firstResult = await myAsyncFunction();
console.log(firstResult); // handles first call
}
More clean way is to wrap it in another async function with same setTimeout inside:
async function myAsyncFunction() {
// imitates async function
return new Promise(resolve => setTimeout(() => resolve('result'), 2000));
}
async function delayLoop() {
let result = await myAsyncFunction(); // waits for it
console.log(result);
setTimeout(delayLoop, 2000); // calls itself after extra 2s
}
delayLoop();
This way you do not have to modify original async function code and put callback right in there.
Both examples will await for async function for 2s before first result, and for 4s between calls - 2s async function delay + 2s extra delay after it.

Related

How do I wait for multiple promises to resolve but also include a set minumum delay?

I am calling two APIs with two different functions and having a setTimeout for both, is it possible to handle their timeouts in such a way that they finish in 10s instead of 15s.
function delay(s){
return new Promise(resolve => setTimeout(resolve,s));
}
async function getXml(){
let ans;
await delay(10000)
const {data} = await axios.get('https://gist.githubusercontent.com/SwayamShah97/a3619c5828ac8ed8085c4ae295a855d9/raw/e4e372552e042bd8bd9e8ab87da93eb030114f86/people.xml');
xml2js.parseString(data, (err, result) => {
if(err) {
throw err;
}
ans = result
});
return ans;
}
async function getPeople(){
await delay(5000)
const { data } = await axios.get('https://gist.githubusercontent.com/SwayamShah97/0f2cb53ddfae54eceea083d4aa8d0d65/raw/d7d89c672057cf7d33e10e558e001f33a10868b2/people.json');
return data; // this will be the array of people objects
}
Is there a way to run this code only in 10s, so that both the APIs get called in 10s time period
Forcing either one or both axios.get() functions to complete below some time limit (other than failing on a timeout), isn't doable, since you don't control the transport.
One thing you can do is force one or more functions to complete at or after some time threshold, with something like this...
function padToTime(promise, interval) {
// delay returns a promise that resolves after an interval
const delay = interval => new Promise(resolve => setTimeout(resolve, interval));
// caller can provide a singular or an array of promises, avoiding the extra .all
let promises = Array.isArray(promise) ? promise : [promise];
return Promise.all([...promises, delay(interval)])
.then(results => results.slice(0, -1));
}
EDIT good idea from #VLAZ to append an extra promise that forces minimal time (then slice its result off later).
The caller can say:
async function getXml(){
// op function, no calls to "delay"
}
async function getPeople(){
// op function, no calls to "delay"
}
// run these both together, have them take *no fewer than* 10s
padToTime([getXml(),getPeople()], 10000).then(results => {
// we'll get here in no fewer than 10sec with the promise all results
})

Rearming a promise when used with setInterval and setTimeout [duplicate]

function first(){
console.log('first')
}
function second(){
console.log('second')
}
let interval = async ()=>{
await setInterval(first,2000)
await setInterval(second,2000)
}
interval();
Imagine that I have this code above.
When I run it, first() and second() will be called at the same time; how do I call second() after first)() returns some data, for example, if first() is done, only then call second()?
Because first() in my code will be working with a big amount of data and if this 2 functions will be calling at the same time, it will be hard for the server.
How do I call second() each time when first() will return some data?
As mentioned above setInterval does not play well with promises if you do not stop it. In case you clear the interval you can use it like:
async function waitUntil(condition) {
return await new Promise(resolve => {
const interval = setInterval(() => {
if (condition) {
resolve('foo');
clearInterval(interval);
};
}, 1000);
});
}
Later you can use it like
const bar = waitUntil(someConditionHere)
You have a few problems:
Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well.
Neither setInterval(), nor the more appropriate setTimeout() return Promises, therefore, awaiting on them is pointless in this context.
You're looking for a function that returns a Promise which resolves after some times (using setTimeout(), probably, not setInterval()).
Luckily, creating such a function is rather trivial:
async function delay(ms) {
// return await for better async stack trace support in case of errors.
return await new Promise(resolve => setTimeout(resolve, ms));
}
With this new delay function, you can implement your desired flow:
function first(){
console.log('first')
}
function second(){
console.log('second')
}
let run = async ()=>{
await delay(2000);
first();
await delay(2000)
second();
}
run();
setInterval doesn't play well with promises because it triggers a callback multiple times, while promise resolves once.
It seems that it's setTimeout that fits the case. It should be promisified in order to be used with async..await:
async () => {
await new Promise(resolve => setTimeout(() => resolve(first()), 2000));
await new Promise(resolve => setTimeout(() => resolve(second()), 2000));
}
await expression causes async to pause until a Promise is settled
so you can directly get the promise's result without await
for me, I want to initiate Http request every 1s
let intervalid
async function testFunction() {
intervalid = setInterval(() => {
// I use axios like: axios.get('/user?ID=12345').then
new Promise(function(resolve, reject){
resolve('something')
}).then(res => {
if (condition) {
// do something
} else {
clearInterval(intervalid)
}
})
}, 1000)
}
// you can use this function like
testFunction()
// or stop the setInterval in any place by
clearInterval(intervalid)
You could use an IFFE. This way you could escape the issue of myInterval not accepting Promise as a return type.
There are cases where you need setInterval, because you want to call some function unknown amount of times with some interval in between.
When I faced this problem this turned out to be the most straight-forward solution for me. I hope it help someone :)
For me the use case was that I wanted to send logs to CloudWatch but try not to face the Throttle exception for sending more than 5 logs per second. So I needed to keep my logs and send them as a batch in an interval of 1 second. The solution I'm posting here is what I ended up using.
async function myAsyncFunc(): Promise<string> {
return new Promise<string>((resolve) => {
resolve("hello world");
});
}
function myInterval(): void {
setInterval(() => {
void (async () => {
await myAsyncFunc();
})();
}, 5_000);
}
// then call like so
myInterval();
Looked through all the answers but still didn't find the correct one that would work exactly how the OP is asked. This is what I used for the same purpose:
async function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (await callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})()
In my example, I also put interval iteration count and the timer itself, just in case the caller would need to do something with it. However, it's not necessary
In my case, I needed to iterate through a list of images, pausing in between each, and then a longer pause at the end before re-looping through.
I accomplished this by combining several techniques from above, calling my function recursively and awaiting a timeout.
If at any point another trigger changes my animationPaused:boolean, my recursive function will exit.
const loopThroughImages = async() => {
for (let i=0; i<numberOfImages; i++){
if (animationPaused) {
return;
}
this.updateImage(i);
await timeout(700);
}
await timeout(1000);
loopThroughImages();
}
loopThroughImages();
Async/await do not make the promises synchronous.
To my knowledge, it's just a different syntax for return Promise and .then().
Here i rewrote the async function and left both versions, so you can see what it really does and compare.
It's in fact a cascade of Promises.
// by the way no need for async there. the callback does not return a promise, so no need for await.
function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
// async function with async/await, this code ...
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})() //... returns a pending Promise and ...
console.log('i do not wait');
// ... is kinda identical to this code.
// still asynchronous but return Promise statements with then cascade.
(() => {
console.log('start again');
return waitInterval(first, 1000).then(() => {
return waitInterval(second, 1000).then(() => {
console.log('finish again');
});
});
})(); // returns a pending Promise...
console.log('i do not wait either');
You can see the two async functions both execute at the same time.
So using promises around intervals here is not very useful, as it's still just intervals, and promises changes nothing, and make things confusing...
As the code is calling callbacks repeatedly into an interval, this is, i think, a cleaner way:
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function executeThroughTime(...callbacks){
console.log('start');
let callbackIndex = 0; // to track current callback.
let timerIndex = 0; // index given to callbacks
let interval = setInterval(() =>{
if (callbacks[callbackIndex](timerIndex++)){ // callback return true when it finishes.
timerIndex = 0; // resets for next callback
if (++callbackIndex>=callbacks.length){ // if no next callback finish.
clearInterval(interval);
console.log('finish');
}
}
},1000)
}
executeThroughTime(first,second);
console.log('and i still do not wait ;)');
Also, this solution execute a callback every secondes.
if the callbacks are async requests that takes more than one sec to resolve, and i can't afford for them to overlap, then, instead of doing iterative call with repetitive interval, i would get the request resolution to call the next request (through a timer if i don't want to harass the server).
Here the "recursive" task is called lTask, does pretty much the same as before, except that, as i do not have an interval anymore, i need a new timer each iteration.
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync1(i) {
console.log(`first pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that first big data'), Math.floor(Math.random()*1000)+ 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{
console.log(`first solved: ${i} ->`, result);
return i==2;
});
}
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync2(i) {
console.log(`second pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that second big data'), Math.floor(Math.random()*1000) + 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{ // promise is resolved
console.log(`second solved: ${i} ->`,result);
return i==4; // return a promise
});
}
function executeThroughTime(...asyncCallbacks){
console.log('start');
let callbackIndex = 0;
let timerIndex = 0;
let lPreviousTime = Date.now();
let lTask = () => { // timeout callback.
asyncCallbacks[callbackIndex](timerIndex++).then((result) => { // the setTimeout for the next task is set when the promise is solved.
console.log('result',result)
if (result) { // current callback is done.
timerIndex = 0;
if (++callbackIndex>=asyncCallbacks.length){//are all callbacks done ?
console.log('finish');
return;// its over
}
}
console.log('time elapsed since previous call',Date.now() - lPreviousTime);
lPreviousTime = Date.now();
//console.log('"wait" 1 sec (but not realy)');
setTimeout(lTask,1000);//redo task after 1 sec.
//console.log('i do not wait');
});
}
lTask();// no need to set a timer for first call.
}
executeThroughTime(simulateAsync1,simulateAsync2);
console.log('i do not wait');
Next step would be to empty a fifo with the interval, and fill it with web request promises...

Calling a setTimeOut after another setTimeOut in Pure JavaScript

I have two setTimeOut here. The second setTimeOut must be called after the first time out.
setTimeout(() => {
mack.classList.add('loaded');
}, 3000);
setTimeout(() => {
location.reload(true);
}, 4000);
But I know that this is not a good solution on this. So I tried to do a Promise:
const timer = ms => new Promise(res => setTimeout(res, ms));
Promise.all([
timer(3000).then(mack.classList.add('loaded')),
timer(1000).then(location.reload(true))
])
This however, did not work. How can I fix this to make it call in order? Also, is there a version to do this using asynchronous?
You are nearly there,
To use timer the way you want, you can use async / await, just remember to be able to use await, it has to be called withing another async function, here I've just placed inside an IFFE to achieve this..
const timer = ms => new Promise(res => setTimeout(res, ms));
(async function () {
console.log('Waiting 3 seconds');
await timer(3000);
console.log('loaded');
await timer(1000);
console.log('reload');
}());
You are creating the promises, and then passing them to Promise.all, which waits for all of them simultaneously.
You want to start the second timer in response to the first timer.
This would do you you want:
timer(3000)
.then(() => {
mack.classList.add('loaded');
return timer(1000);
}).then(() => location.reload(true));
If you wanted this in an an async function:
async function example() {
await timer(3000);
mack.classList.add('loaded');
await timer(1000);
location.reload(true);
}
example().then(() => console.log('async function complete!'));

JavaScript Sequential function execution for multiple functions

I (first time JavaScript user since yesterday) managed to get JavaScript to run functions in sequential execution order (see code below) (credit to #CertainPerformance). I need to use the fastFunction in multiple slowFunctions. The current solution does not seem DRY (do not repeat yourself) to me and at the same time it does not guarantee the exectution order of slowFunction1 and then slowFunction2. What is the DRY solution to this problem in JavaScript? Can I force JavaScript to always run in sequential mode by some configuration? Using nested callbacks does not seem to be the most intelligent solution to me.
function fastFunction(message) {
console.log(message);
}
function slowFunction1(callback, message) {
setTimeout(() => {
console.log('slowFunction1!');
callback(message);
}, 10000);
}
function slowFunction2(callback, message) {
setTimeout(() => {
console.log('slowFunction2!');
callback(message);
}, 1000);
}
slowFunction1(fastFunction, 'fast_Function');
slowFunction2(fastFunction, 'fast_Function');
With async/await you can sequence asynchronous tasks as follows:
// Very handy utility function to get a promise that resolves after a given delay
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function fastFunction(message) {
console.log(message);
}
async function slowFunction1(callback, message) {
console.log('slowFunction1!');
await delay(2000); // two seconds
callback(message);
}
async function slowFunction2(callback, message) {
console.log('slowFunction2!');
await delay(1000); // one second
callback(message);
}
(async function() {
// Put all your logic here, and await promises...
await slowFunction1(fastFunction, 'fast_Function');
await slowFunction2(fastFunction, 'fast_Function');
})(); // execute immediately
Now you will have the delays happening one after the other completes, so 2+1=3 seconds in (approximate) total execution time.
This mimics most what you had as pattern, but once you are using promises, you don't need the callback pattern anymore and can do it like this:
// Very handy utility function to get a promise that resolves after a given delay
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function fastFunction(message) {
console.log(message);
}
(async function() {
console.log('slow part 1');
await delay(2000); // two seconds
fastFunction('fast_function');
console.log('slow part 2');
await delay(1000); // one second
fastFunction('fast_function');
})(); // execute immediately

Why does my timeout wait until after the await completes but my log does not?

If I have something like this setup:
<-- language: lang-javascript -->
console.clear();
// noprotect
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
async function asyncFunction() {
const result = await fetchSomething();
console.log('waiting');
setTimeout(()=>console.log('waiting?'), 250);
return result + ' 2';
}
asyncFunction().then(result => console.log(result));
And my output looks like:
"waiting"
"future value 2"
"waiting?"
I would expect the waiting? to execute before the result completes, but for some reason it waits on the function. What makes one wait but the other execute?
It is a feature of Asynchronous programming.
You have to add await and wrap your setTimeout(()=>console.log('waiting?'), 250); with a function which returns Promise in order to make it look like it was evaluated continuously.
Something like:
await ((ms) =>{
console.log('waiting?');
return new Promise(resolve => setTimeout(resolve, ms));
})(250);
Or:
await (() => new Promise(resolve => setTimeout(()=>{
console.log('waiting?');
resolve();
}, 250)))();
Mind you, JS has a single threaded run-time engine, so it interrupts evaluation when original script reaches it's end.
And function in setTimeout(function, timeout) is evaluated by JS when it has a first chance and time is right.
So your function was interrupted twice and was resumed twice.
The call to log "waiting?" is started by a setTimeout after the await has finished, so after the 500ms in fetchSomething have already passed. It will only execute 250ms after fetchSomething has returned. That is enough time for asyncFunction to return.
If you want to see a different behaviour, start the timer for logging "waiting?" before calling await:
async function asyncFunction() {
setTimeout(()=>console.log('waiting?'), 250);
const result = await fetchSomething();
console.log('waiting');
return result + ' 2';
}

Categories