如何在JavaScript for循环中添加延迟?邮件发送场景遇异常
问题:JavaScript循环中无法添加延迟实现邮件推送限速
我需要遍历邮箱列表,逐个把邮件发送到后端做新闻邮件推送,为了避免发送过快,想在循环里加暂停,但现在delay方法要等整个循环跑完才会执行。我是JavaScript新手,搞不懂为什么会这样,也不太理解涉及let和阻塞的方案。相关代码如下:
async sendNewsletter (event) { //Loop over each email and gather the html content followed by sending it to backend let counter = 0 for (const option of this.includedEmailList.options) { //Call the backend const response = await fetch('url', { method:'POST', credentials:'same-origin', headers:{ 'Content-Type': 'application/json', 'Accept' : 'application/json', }, redirect: 'follow', body: JSON.stringify({ 'emailSubject' : this.emailSubject.value, 'email' : option.value, 'plainTextEmail' : this.emailPlainText.value, 'htmlEmail' : this.hiddenHTML.innerHTML}), }); const responseJson = await response.json(); console.log(responseJson['status']); this.emailSentStatus.innerHTML = responseJson['status']; //I need to delay the loop counter++; this.delay(10000 * counter); } //Everything has been sent. Leave a message and disable the submission button to prevent emailSentStatus.innerHTML = `all emails have been sent!`; } delay (timeMilliseconds) { //To be used in for loop to delay the loop and to slow down setTimeout(() => { console.log('in delay'); console.log(timeMilliseconds);}, timeMilliseconds); }
解决方案
为什么delay会在循环结束后执行?
核心原因是setTimeout是异步非阻塞的:调用setTimeout时,只是把里面的回调函数放到浏览器的事件队列里,当前的循环会继续执行,不会等定时器到期。而且你现在的delay方法没有返回任何异步对象,就算加了await也无法暂停循环。
怎么改才能让循环逐个延迟执行?
只需要两步:
- 把
delay改造成返回Promise的方法,这样就能用await来等待延迟完成; - 在循环里用
await调用delay,让循环暂停。
另外你之前用counter * 10000会导致延迟时间越来越长(第一次10秒,第二次20秒...),如果只是想每次发送后间隔固定时间,直接用固定值就行。
修改后的完整代码:
async sendNewsletter (event) { // 遍历邮箱列表,逐个发送邮件到后端 for (const option of this.includedEmailList.options) { // 调用后端接口发送邮件 const response = await fetch('url', { method:'POST', credentials:'same-origin', headers:{ 'Content-Type': 'application/json', 'Accept' : 'application/json', }, redirect: 'follow', body: JSON.stringify({ 'emailSubject' : this.emailSubject.value, 'email' : option.value, 'plainTextEmail' : this.emailPlainText.value, 'htmlEmail' : this.hiddenHTML.innerHTML }), }); const responseJson = await response.json(); console.log(responseJson['status']); this.emailSentStatus.innerHTML = responseJson['status']; // 等待10秒再执行下一次循环 await this.delay(10000); } // 所有邮件发送完成,更新状态(注意加this.) this.emailSentStatus.innerHTML = `所有邮件已发送完成!`; } // 改造delay方法,返回Promise以支持await delay (timeMilliseconds) { return new Promise(resolve => { setTimeout(() => { console.log('延迟结束'); resolve(); // 延迟完成后通知await继续执行 }, timeMilliseconds); }); }
关键说明
await this.delay(10000)会让循环暂停,直到delay里的setTimeout执行完毕并调用resolve();- 去掉了
counter,如果确实需要每次延迟递增,再重新添加即可; - 最后一行的
emailSentStatus要加this.,否则会找不到这个实例属性。
内容的提问来源于stack exchange,提问作者jxw
相关产品推荐
相关产品推荐

