Venkatesh
Venkatesh

Reputation: 13

Async/Await is not working as expected with for loop statement

I am new to Javascript and nodejs. While trying to understand how promises and callbacks work, i tried calling a function in a 'for' loop as shown below. What i was trying to achieve is to print 'i' value every 2 seconds using promises. However, the program waits for 2 seconds and prints i value 3 times and exits.

for(let i = 0; i < 3 ; i++){
    func(callback,i);
}

async function func(callback,i){
   await callback(i);
}
function callback(i){
    return new Promise((res,rej) =>{
        setTimeout(()=>{
            console.log('i = ',i)
            res();
        }, 2000);

    })
}

Can anybody please help me understand why this is happening?

Upvotes: 1

Views: 1950

Answers (5)

Ever Dev
Ever Dev

Reputation: 2162

Here are some functions which help you understand how promises work with Arrays, where we make common mistakes.

function promiseFunction(v) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(v), 1000);
    });
}
function f1() {
    [1, 2, 3, 4]. forEach(async(i) => {
        const v = await promiseFunction(i);
        console.log(`-f1 v- ${v}<br/>`);
    });
    console.log('all done<br/>');
}
async function f2() {
    await Promise.all([1, 2, 3, 4].map(async(i) => {
        const v = await promiseFunction(i);
        console.log(`-f2 v- ${v}<br/>`);
    }));
    console.log('all done<br/>');
}
async function f3() {
    await [1, 2, 3, 4].reduce((p, i) => {
        return p.then(async () => {
            const v = await promiseFunction(i);
            console.log(`-f3 v- ${v}<br/>`);
        });
    }, Promise.resolve());
    console.log('all done<br/>');
}

async function func() {
   console.log('f1');
   await f1();
   console.log('f2');
   await f2();
   console.log('f3');
   await f3();
}

func();

f1() will print all done first and then print 1,2,3,4 at once after a second.
f2() will print 1,2,3,4 at once after a second then print all done.
f3() will print 1,2,3,4 in every second, and then print all done.

Upvotes: -1

Andriy
Andriy

Reputation: 15472

You can just wrap your loop with async immediately executed function and add await within it (as was already suggested here):

(async () => {
  for(let i = 0; i < 3 ; i++){
    await callback(i);
  }
})();

function callback(i){
  return new Promise((res,rej) =>{
    setTimeout(()=>{
      console.log('i = ',i)
      res();
    }, 2000);
  })
}

Here is my original answer before Bergi's edit:

(async () => {
  for(let i = 0; i < 3 ; i++){
    await func(callback,i);
  }
})()

async function func(callback,i){
  await callback(i);
}

function callback(i){
  return new Promise((res,rej) =>{
    setTimeout(()=>{
      console.log('i = ',i)
      res();
    }, 2000);
  })
}

Upvotes: 3

alfredopacino
alfredopacino

Reputation: 3241

You are pretty close. The missing information is async functions (your func()) implicitly returns an AsyncFunction object which implicitly returns a Promise itself too (doc).

Your code using immediately invoked function expression would be

(async () => {
    for(let i = 0; i < 3 ; i++){
        await func(callback,i);
    }
})()

async function func(callback,i){
   await callback(i);
}
function callback(i){
    return new Promise((res,rej) =>{
        setTimeout(()=>{
            console.log('i = ',i)
            res();
        }, 2000);
    })
}

Please note now in most of the browsers is natively available the construct for await..of (doc). You can try experimenting this one too.

Upvotes: 3

Molkan
Molkan

Reputation: 564

You need to wait for async function to complete

for(let i = 0; i < 3 ; i++){
    await func(callback,i);
}

But since you can't use await keyword in the global scope, you will need to wrap your for loop in an async function, and than call it

async function myTest(){
    for(let i = 0; i < 3 ; i++){
        await func(callback,i);
    }
}
myTest()

Upvotes: -1

Muhammad Salman Abid
Muhammad Salman Abid

Reputation: 50

You async function func also returns a promise so you need the await keyword before its call.

for(let i = 0; i < 3 ; i++){
    await func(callback,i);
}

Upvotes: -2

Related Questions