0

When I run the following code:

b = async attempt => {
  if (attempt == 5) {
    return;
  }
  console.log("Starting attempt:", attempt);
  b(attempt + 1).then(() => {
    console.log("Finished attempt:", attempt);
  });
};
b(0);

The output is:

Starting attempt: 0
Starting attempt: 1
Starting attempt: 2
Starting attempt: 3
Starting attempt: 4
Finished attempt: 4
Finished attempt: 3
Finished attempt: 2
Finished attempt: 1
Finished attempt: 0

However, I want to call another promise a before each recursive call as follows:

a = Promise.resolve();
b = async attempt => {
  if (attempt == 5) {
    return;
  }
  console.log("Starting attempt:", attempt);
  a.then(() => {
    b(attempt + 1);
  }).then(() => {
    console.log("Finished attempt:", attempt);
  });
};
b(0);

Now the output is:

Starting attempt: 0
Starting attempt: 1
Starting attempt: 2
Finished attempt: 0
Starting attempt: 3
Finished attempt: 1
Starting attempt: 4
Finished attempt: 2
Finished attempt: 3
Finished attempt: 4

How can I modify the second code block to ensure that the output is the same as the first code block?

Emma
  • 27,428
  • 11
  • 44
  • 69
J. Doe
  • 3
  • 3

3 Answers3

0

If I understand your question correctly, then the issue is cause by nested promises and calls to asynchronous functions (ie b(attempt + 1)) not blocking execution of the enclosing function. This in turn produces unordered, and unexpected logging results.

Consider revising the call to b(), by return the promise of b(attempt + 1); to ensure that the following log: console.log("Finished attempt:", attempt) is executed after completion of b() completes:

const a = Promise.resolve();
const b = async attempt => {
  if (attempt == 5) {
    return;
  }
  console.log("Starting attempt:", attempt);
  
  await a.then(() => {
  
    /* 
    Return the project of b() here, has similar effect
    to that of await keyword
    */
    return b(attempt + 1); 
  }).then(() => {
    console.log("Finished attempt:", attempt);
  });
};

b(0);
Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
0

You are breaking the promise chain by not returning an inner promise a.then(() => {b(attempt + 1);}). Adding return statement should help.

Also I would recommend not to mix async/await syntax with .then and callbacks.

The following code looks more "synchronous" and does the trick.

const a = Promise.resolve();
const b = async attempt => {
  if (attempt == 5) {
    return;
  }
  console.log("Starting attempt:", attempt);
  
  await a
  
  await b(attempt + 1)
  
  console.log(`Finished attempt: ${attempt}`)
};

b(0);
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
0

You forgot to return the promise chains that you are creating:

const b = attempt => {
  if (attempt == 5) {
    return Promise.resolve();
  }
  console.log("Starting attempt:", attempt);
  const a = Promise.resolve();
  return a.then(() => {
//^^^^^^
    return b(attempt + 1);
//  ^^^^^^
  }).then(() => {
    console.log("Finished attempt:", attempt);
  });
};
b(0).then(() => {
  console.log("done");
});

Only with those returns, the caller of the function will be able to wait for the promise. Your first snippet has the same issue. It would've been more obvious if you had omitted the async keyword, so that it was clear that b() didn't return a promise.

If you want to use async/await, your code should look like this:

const b = async attempt => {
  if (attempt == 5) {
    return;
  }
  console.log("Starting attempt:", attempt);
  await Promise.resolve();
//^^^^^
  await b(attempt + 1);
//^^^^^
  console.log("Finished attempt:", attempt);
};
b(0).then(() => {
  console.log("done");
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375