3

I expected the output: A, B, C. But it doesn't work. Provided that the function handleClick(element) cannot be changed, how can I change the other functions to make sure that all code execute in sequence and output A, B, C as expected?

async function handleClick(element) {
  setTimeout(function(){
    console.log(`Click on Element_${element}`);
  }
  , Math.random(5)*1000);
}

async function clickLetter(letter) {
  await handleClick(letter);
}

async function clickGroup(group) {
  await handleClick(group);
}

const letters = ['A', 'B', 'C'];

function clickLetters(letters, fn) {
  let index = 0;
  return new Promise(function(resolve, reject) {
    function next() {
      if (index < letters.length) {
        fn(letters[index++]).then(next, reject);
      } else {
        resolve();
      }
    }
    next();
  });
}

clickLetters(letters, clickLetter);

5
  • You could use the async library, caolan.github.io/async/docs.html#eachSeries Commented Dec 2, 2017 at 11:08
  • You got your answer? Commented Dec 2, 2017 at 15:30
  • @Cristy No, you can't. It's useless with promises. Commented Dec 2, 2017 at 16:36
  • @Bergi Can you elaborate? Couldn't it be used like async.eachSeries(letters, clickLetter) ? Commented Dec 2, 2017 at 20:08
  • @Cristy No, it couldn't. clickLetter returns a promise, it does not take a node-style callback. Commented Dec 2, 2017 at 20:59

3 Answers 3

1

The setTimeout function is asynchronous and the result is returned immediately, you need to wrap this inside a promise constructor and then resolve it.

function handleClick(element) {
   return new Promise((resolve, reject) => {
     setTimeout(() => {
       console.log(`Clicked on Element_${element}`);
       resolve();
     }, Math.random(5) * 1000);
  });
}

async function clickLetter(letter) {
  await handleClick(letter);
}

async function clickGroup(group) {
  await handleClick(group);
}

const letters = ['A', 'B', 'C'];

async function clickLetters(letters, fn) {
  for(let i = 0; i < letters.length; i++) {
    await clickLetter(letters[i]);
  }
}

clickLetters(letters, clickLetter);

Sign up to request clarification or add additional context in comments.

2 Comments

So if I want the asynchronous function to wait for finish, I should wrap it inside a promise? if asynchrounous function contains multi-level nested asynchronous calls, I have to wrap each level with Promise?
You would want to do that whenever you're dealing with callback functions like in the case of setTimeout first argument.
1

As mentioned, the async library is/was a way to do it.

If you want to keep going with your current solution, the problem was in your handleClick() where the implicit promise was returning immediately, before the timeout. Then, depending on the random timeout for each pass, it would result in an out or ordr execution. The fix is just resolve the promise then it times out.

function handleClick(element) {
  return new Promise(resolve => {
    setTimeout(function(){
      console.log(`Click on Element_${element}`);
      resolve();
    }
    , Math.random(5)*1000);
  });
}

async function clickLetter(letter) {
  await handleClick(letter);
}

function clickLetters(letters, fn) {
  let index = 0;
  return new Promise(function(resolve, reject) {
    function next() {
      if (index < letters.length) {
        fn(letters[index++]).then(next, reject);
      } else {
        resolve();
      }
    }
    next();
  });
}

const letters = ['A', 'B', 'C'];
clickLetters(letters, clickLetter);

Comments

1

Read about async and await here.

Here is the code. Explanation is at the end.

async function handleClick(element) {
   return new Promise((resolve, reject) => {
     setTimeout(function(){
       console.log(`Click on Element_${element}`);
       resolve();
     }, Math.random(5)*1000);
  });
}

async function clickLetter(letter) {
  await handleClick(letter);
}

async function clickGroup(group) {
  await handleClick(group);
}

const letters = ['A', 'B', 'C'];

async function clickLetters(letters, fn) {
  for(let i=0; i<letters.length; i++) {
    await clickLetter(letters[i]);
  }
}

clickLetters(letters, clickLetter);

Explanation of what changes you will need -

  • STEP 1 - Convert your handleClick function to return a Promise.

  • STEP 2 - Use await in your clickLetters function to print the values sequentially.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.