Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add milliseconds delay to Array.map calls which returns Array of promises

My need is simple. I would like to delay calls to sendEmail by 100 milliseconds. The email service provider permits at most sending 10 emails per second.

Note, however, though .map is synchronous, it immediately returns a Promise.

I have tried setTimeout to no avail, such as setTimeout(() => resolve(x), 100) and setTimeout(() => {return new Promise....}, 100).

Thoughts?

const promises = userEmailArray.map((userEmail) => {
  return new Promise((resolve, reject) => {
      ....
      mailer.sendEmail(userEmail);
      return resolve();
    });
  });
});
...
Promise.all(promises).then(() => resolve()).catch(error => reject(error));
like image 421
user1322092 Avatar asked Dec 04 '17 02:12

user1322092


1 Answers

You already have a 'queue' of sorts: a list of addresses to send to. All you really need to do now is pause before sending each one. However, you don't want to pause for the same length of time prior to each send. That'll result in a single pause of n ms, then a whole raft of messages being sent within a few ms of each other. Try running this and you'll see what I mean:

const userEmailArray = [ 'one', 'two', 'three' ]
const promises = userEmailArray.map(userEmail =>
  new Promise(resolve =>
    setTimeout(() => {
      console.log(userEmail)
      resolve()
    }, 1000)
  )
)
Promise.all(promises).then(() => console.log('done'))

Hopefully you saw a pause of about a second, then a bunch of messages appearing at once! Not really what we're after.

Ideally, you'd delegate this to a worker process in the background so as not to block. However, assuming you're not going to do that for now, one trick is to have each call delayed by a different amount of time. (Note that this does not solve the problem of multiple users trying to process large lists at once, which presumably is going to trigger the same API restrictions).

const userEmailArray = [ 'one', 'two', 'three' ]
const promises = userEmailArray.map((userEmail, i) =>
  new Promise(resolve =>
    setTimeout(() => {
      console.log(userEmail)
      resolve()
    }, 1000 * userEmailArray.length - 1000 * i)
  )
)
Promise.all(promises).then(() => console.log('done'))

Here, you should see each array element be processed in roughly staggered fashion. Again, this is not a scaleable solution but hopefully it demonstrates a bit about timing and promises.

like image 121
Rich Churcher Avatar answered Oct 12 '22 20:10

Rich Churcher