41
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?

Adriano
  • 3,363
  • 4
  • 32
  • 46
Andrey Radkevich
  • 657
  • 1
  • 8
  • 13
  • 1
    setInterval never resolves since it's a continous repeater. Not sure if you've thought this through. What exactly do you want to achieve? If you want to run calls every 2 seconds and reacting to it, you might be better off writing a solution with RxJs and using Observables. – Lars Holdaas Sep 05 '18 at 11:54
  • Can you explicate your use case? – k0pernikus Sep 05 '18 at 13:34
  • When server started , I want to call some functions with some interval - for example each 15 minutes - but this functions works with big data , I can run 2 functions in the same time , but it will be hard for the server to work with I think , I want to make the same run functions with some interval but , with waiting on when first fucntion done ... for example I have first function after 15 min this function executed , it is executes and second function wait , only when first function done , second function start , and so on each time . Hope it is clear . – Andrey Radkevich Sep 05 '18 at 13:42

6 Answers6

47

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)
Zhora
  • 549
  • 3
  • 11
mdikici
  • 1,164
  • 2
  • 12
  • 20
  • 2
    Though I think the question asked isn't clearly stated, this answer points out the fallacy stated by several people that `setInterval` doesn't play well with promises; it can play very well if the correct logic is supplied (just as any code has its own requirements to run correctly). I fixed some syntax errors but I think the gist of this answer provides better information than the others. (I don't think it really answers the original question but I'm not sure I know exactly what that question is asking myself.) – Zhora Jun 07 '19 at 14:55
  • Then condition should be a callback function. – Long Nguyen Mar 27 '21 at 17:26
  • Really appreciate this! Thank you – WhoIsCarlo Apr 08 '21 at 18:57
35

You have a few problems:

  1. Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well.
  2. 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();
Madara's Ghost
  • 165,920
  • 50
  • 255
  • 304
5

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));
}
Estus Flask
  • 179,509
  • 61
  • 360
  • 499
3

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)
1

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();
G. Spasov
  • 31
  • 3
0

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();
dpetraz
  • 1
  • 1
  • Combining recursion with loops like this is weird though. Why don't you simply nest two loops? – Bergi Apr 14 '22 at 22:40
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31540469) – 8ctopus Apr 21 '22 at 17:43