向Array.map调用添加毫秒级延迟,该延迟将返回promise数组 [英] Add milliseconds delay to Array.map calls which returns Array of promises
问题描述
我的需求很简单.我想将对sendEmail
的调用延迟100毫秒.电子邮件服务提供商最多允许每秒发送10封电子邮件.
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.
请注意,尽管.map
是同步的,但它立即返回Promise
.
Note, however, though .map
is synchronous, it immediately returns a Promise
.
我尝试setTimeout
都无济于事,例如setTimeout(() => resolve(x), 100)
和setTimeout(() => {return new Promise....}, 100)
.
I have tried setTimeout
to no avail, such as setTimeout(() => resolve(x), 100)
and setTimeout(() => {return new Promise....}, 100)
.
有想法吗?
const promises = userEmailArray.map((userEmail) => {
return new Promise((resolve, reject) => {
....
mailer.sendEmail(userEmail);
return resolve();
});
});
});
...
Promise.all(promises).then(() => resolve()).catch(error => reject(error));
推荐答案
有很多方法可以解决此问题.我可能自己会使用一个递归链式promise,然后可以根据上一次调用的结束时间更精确地使用一个计时器,并且可以使用promise来调用它并处理错误的传播.
There are a bunch of different ways to approach this. I'd probably just use a recursive chained promise myself and then you can more precisely use a timer based on the finish from the previous call and you can use promises for calling it and handling propagation of errors.
我在这里假设您的mailer.sendEmail()
遵循node.js回调调用约定,因此我们需要对其进行承诺化".如果它已经返回了承诺,那么您可以直接使用它代替我创建的sendEmail()
函数.
I've assumed here that your mailer.sendEmail()
follows the node.js callback calling convention so we need to "promisify" it. If it already returns a promise, then you can use it directly instead of the sendEmail()
function that I created.
无论如何,这里有很多不同的方法.
Anyways, here are a bunch of different approaches.
延迟后调用相同功能(递归延迟)
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
function sendAll(array) {
let index = 0;
function next() {
if (index < array.length) {
return sendEmail(array[index++]).then(function() {
return delay(100).then(next);
});
}
}
return Promise.resolve().then(next);
}
// usage
sendAll(userEmailArray).then(() => {
// all done here
}).catch(err => {
// process error here
});
使用setInterval来控制步速
您也可以只使用setInterval来每隔100ms发出一个新请求,直到数组为空:
You could also just use a setInterval to just launch a new request every 100ms until the array was empty:
// promisify
let sendEmail = util.promisify(mailer.sendEmail);
function sendAll(array) {
return new Promise((resolve, reject) => {
let index = 0;
let timer = setInterval(function() {
if (index < array.length) {
sendEmail(array[index++]).catch(() => {
clearInterval(timer);
reject();
});
} else {
clearInterval(timer);
resolve();
}
}, 100);
})
}
使用等待暂停循环
而且,您可以在ES6中使用await
来暂停"循环:
And, you could use await
in ES6 to "pause" the loop:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
// assume this is inside an async function
for (let userEmail of userEmailArray) {
await sendEmail(userEmail).then(delay.bind(null, 100));
}
将.reduce()
与承诺一起用于对阵列的访问顺序
Use .reduce()
with Promises to Sequence Access to Array
如果您不想累积结果数组,而只是想排序,那么一种典型的方法是使用由.reduce()
驱动的承诺链:
If you aren't trying to accumulate an array of results, but just want to sequence, then a canonical ways to do that is using a promise chain driven by .reduce()
:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
userEmailArray.reduce(function(p, userEmail) {
return p.then(() => {
return sendEmail(userEmail).then(delay.bind(null, 100));
});
}, Promise.resolve()).then(() => {
// all done here
}).catch(err => {
// process error here
});
同时使用Bluebird功能进行并发控制和延迟
蓝鸟诺言库内置了几个有用的功能,可在此处提供帮助:
The Bluebird promise library has a couple useful features built in that help here:
const Promise = require('Bluebird');
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = Promise.promisify(mailer.sendEmail);
Promise.map(userEmailArray, userEmail => {
return sendEmail(userEmail).delay(100);
}, {concurrency: 1}).then(() => {
// all done here
}).catch(err => {
// process error here
});
请注意,同时使用{concurrency: 1}
功能来控制正在处理的请求数和内置的.delay(100)
许诺方法.
Note the use of both the {concurrency: 1}
feature to control how many requests are in-flight at the same time and the built-in .delay(100)
promise method.
创建通常可以使用的序列助手
而且,创建一个小的辅助函数来对数组进行排序(在两次迭代之间有延迟)可能会很有用:
And, it might be useful to just create a little helper function for sequencing an array with a delay between iterations:
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
async function runSequence(array, delayT, fn) {
for (item of array) {
await fn(item).then(data => {
return delay(delayT, data);
});
}
}
然后,您可以在需要时使用该助手:
Then, you can just use that helper whenever you need it:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
runSequence(userEmailArray, sendEmail, 100).then(() => {
// all done here
}).catch(err => {
// process error here
});
这篇关于向Array.map调用添加毫秒级延迟,该延迟将返回promise数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!