javascriptjames
javascriptjames

Reputation: 124

How do I loop over 3 setInterval functions but chain them as promises so the resolve and move on?

Assuming the first(), second(), and third() functions work, how do I chain them together asynchronously in a loop so that the first resolves, then the second has access to the DOM after first resolves, etc.

So I can click through similar DOM items and take 3 actions over and over?

// first() pops up a list of buttons I can click
var first = setInterval(function() {
  var expand = document.querySelector('a');
  expand.click();
}, 1000);

// second() chooses an item and clicks it
var second = setInterval(function() {
  var something = document.querySelector('a');
  something.click();
}, 1000);

// third then chooses the confirm button
var third = setInterval(function() {
  var area = document.querySelector('.class');
  var confirm = document.querySelector('.confirm');
  confirm.click();
}, 1000);


var times = 100;
var ran = 0;

while (ran < times) {
  return new Promise(function(res, rej) {
    first()
  }).then(function() {
    second()
  }).then(function() {
    third();
  }).then(function() {
    fourth();
  });

  ran++;
  console.log(ran);
}

Upvotes: 2

Views: 845

Answers (1)

Rico Kahler
Rico Kahler

Reputation: 19262

I think the simplest way to solve your problem is to wrap setTimeout in a Promise and then use async/await. That way you can use the same control flow you already have set in place.

You should use setTimeout instead of setInterval. setInterval runs periodically at set intervals of time but your while loop already runs your code multiple times so you should use setTimeout instead which only runs the callback once.

To make things simpler, I would wrap setTimeout in a Promise so you can use async and await.

async is a modifier you place on functions that allows you to use the key word await. What await does is it unwraps promises and waits for them to resolve before running the next bit of code. It allows you to use simple synchronous control flow even if the code is actually asynchronous.

What I did is create an async function main (you can call it anything you want though) that awaits the wrapped setTimeout function. Now that function will wait until the time has finished and then run the next piece of code.

Hope that helps!

a Note on browser compatibility: Async and await is from the lastest javascript standard so it won't work on IE. You can use babel to converter newer javascript to older javascript if you need to.

function time(milliseconds) {
  return new Promise(resolve => setTimeout(() => resolve(), milliseconds));
}

function first () {
  // const expand = document.querySelector('a');
  // expand.click();
  console.log('first ran');
}
function second() {
  // const something = document.querySelector('a');
  // something.click();
  console.log('second ran');
}
function third() {
  // const area = document.querySelector('.class');
  // const confirm = document.querySelector('.confirm');
  // confirm.click();
  console.log('third ran');
}

async function main() {
  const times = 100;
  let ran = 0;

  while (ran < times) {
    first();
    await time(1000);
    second();
    await time(1000);
    third();
    await time(1000);
    ran++;
    console.log(ran);
  }
}

main();


From your comment:

Thanks! Also curious, the best possible solution for me would be to be able to step through these using yield so I can call main().next(). Do you know how to structure that as a generator with the same time promise? Would love the help on that

I don't think this is what you want but it sort of satisfies your requirements. Maybe I have to think about this more...

Anyway, run is now a function that returns a promise and in the make function, it yields the promise run returns. So now in another async function you can await their values.

Again, I don't think this is what you want but I can give you another suggestion if you clarify your intent.

function time(milliseconds) {
  return new Promise(resolve => setTimeout(() => resolve(), milliseconds));
}

function first () {
  // const expand = document.querySelector('a');
  // expand.click();
  console.log('first ran');
}
function second() {
  // const something = document.querySelector('a');
  // something.click();
  console.log('second ran');
}
function third() {
  // const area = document.querySelector('.class');
  // const confirm = document.querySelector('.confirm');
  // confirm.click();
  console.log('third ran');
}

async function run() {
  first();
  await time(1000);
  second();
  await time(1000);
  third();
  await time(1000);
}

function* make() {
  const times = 100;
  let ran = 0;

  while (ran < times) {
    yield run();
    ran++;
    console.log(ran);
  }
}

async function main() {
  const iterator = make();
  await iterator.next().value;
  await iterator.next().value;
  await iterator.next().value;
  await iterator.next().value;
}

main();

Upvotes: 2

Related Questions